From c68f5dd1fae0fae7bf803a10859d0ec588f92861 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 25 Mar 2015 15:13:40 -0700 Subject: [PATCH 01/23] added various controls for the convex hull generator --- libraries/shared/src/Extents.h | 6 +++++- tools/CMakeLists.txt | 2 +- tools/vhacd/src/VHACDUtil.cpp | 32 +++++++++++++++++++++++++++---- tools/vhacd/src/VHACDUtil.h | 4 +++- tools/vhacd/src/VHACDUtilApp.cpp | 33 +++++++++++++++++++++++++++++--- 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/libraries/shared/src/Extents.h b/libraries/shared/src/Extents.h index 95f242c30b..66b33114f7 100644 --- a/libraries/shared/src/Extents.h +++ b/libraries/shared/src/Extents.h @@ -14,6 +14,7 @@ #define hifi_Extents_h #include +#include #include #include "StreamUtils.h" @@ -46,6 +47,9 @@ public: /// rotate the extents around orign by rotation void rotate(const glm::quat& rotation); + glm::vec3 size() const { return maximum - minimum; } + float largestDimension () const {glm::vec3 s = size(); return glm::max(s[0], s[1], s[2]); } + /// \return new Extents which is original rotated around orign by rotation Extents getRotated(const glm::quat& rotation) const { Extents temp = { minimum, maximum }; @@ -68,4 +72,4 @@ inline QDebug operator<<(QDebug debug, const Extents& extents) { } -#endif // hifi_Extents_h \ No newline at end of file +#endif // hifi_Extents_h diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 5c7c306a62..ba2938aaa6 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -8,5 +8,5 @@ set_target_properties(scribe PROPERTIES FOLDER "Tools") find_package(VHACD) if(VHACD_FOUND) add_subdirectory(vhacd) -set_target_properties(vhacd PROPERTIES FOLDER "Tools") +# set_target_properties(vhacd PROPERTIES FOLDER "Tools") endif() diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp index bc9ad09bde..c98a095f47 100644 --- a/tools/vhacd/src/VHACDUtil.cpp +++ b/tools/vhacd/src/VHACDUtil.cpp @@ -57,8 +57,15 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re if (triangles.count() <= 0){ continue; } + + AABox aaBox; + foreach (glm::vec3 p, vertices) { + aaBox += p; + } + results->perMeshVertices.append(vertices); results->perMeshTriangleIndices.append(triangles); + results->perMeshLargestDimension.append(aaBox.getLargestDimension()); count++; } @@ -66,23 +73,40 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re return true; } -bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results)const{ +bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, + vhacd::ComputeResults *results, + int startMeshIndex, int endMeshIndex, float minimumMeshSize) const { VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD(); int meshCount = meshes->meshCount; int count = 0; std::cout << "Performing V-HACD computation on " << meshCount << " meshes ..... " << std::endl; - for (int i = 0; i < meshCount; i++){ + if (startMeshIndex < 0) { + startMeshIndex = 0; + } + if (endMeshIndex < 0) { + endMeshIndex = meshCount; + } + for (int i = startMeshIndex; i < endMeshIndex; i++){ + qDebug() << "--------------------"; std::vector vertices = meshes->perMeshVertices.at(i).toStdVector(); std::vector triangles = meshes->perMeshTriangleIndices.at(i).toStdVector(); int nPoints = (unsigned int)vertices.size(); int nTriangles = (unsigned int)triangles.size() / 3; - std::cout << "Mesh " << i + 1 << " : "; + const float largestDimension = meshes->perMeshLargestDimension.at(i); + + qDebug() << "Mesh " << i << " -- " << nPoints << " points, " << nTriangles << " triangles, " + << "size =" << largestDimension; + + if (largestDimension < minimumMeshSize || largestDimension > 1000) { + qDebug() << " Skipping..."; + continue; + } // compute approximate convex decomposition bool res = interfaceVHACD->Compute(&vertices[0].x, 3, nPoints, &triangles[0], 3, nTriangles, params); if (!res){ - std::cout << "V-HACD computation failed for Mesh : " << i + 1 << std::endl; + qDebug() << "V-HACD computation failed for Mesh : " << i; continue; } count++; //For counting number of successfull computations diff --git a/tools/vhacd/src/VHACDUtil.h b/tools/vhacd/src/VHACDUtil.h index b0b9da9720..304a7a2c5d 100644 --- a/tools/vhacd/src/VHACDUtil.h +++ b/tools/vhacd/src/VHACDUtil.h @@ -34,12 +34,14 @@ namespace vhacd { int meshCount; QVector> perMeshVertices; QVector> perMeshTriangleIndices; + QVector perMeshLargestDimension; } LoadFBXResults; class VHACDUtil { public: bool loadFBX(const QString filename, vhacd::LoadFBXResults *results); - bool computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results)const; + bool computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, + vhacd::ComputeResults *results, int startMeshIndex, int endMeshIndex, float minimumMeshSize) const; ~VHACDUtil(); }; diff --git a/tools/vhacd/src/VHACDUtilApp.cpp b/tools/vhacd/src/VHACDUtilApp.cpp index b4c141acae..a1a04fd127 100644 --- a/tools/vhacd/src/VHACDUtilApp.cpp +++ b/tools/vhacd/src/VHACDUtilApp.cpp @@ -98,6 +98,15 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : const QCommandLineOption outputFilenameOption("o", "output file", "filename.obj"); parser.addOption(outputFilenameOption); + const QCommandLineOption startMeshIndexOption("s", "start-mesh index", "0"); + parser.addOption(startMeshIndexOption); + + const QCommandLineOption endMeshIndexOption("e", "end-mesh index", "0"); + parser.addOption(endMeshIndexOption); + + const QCommandLineOption minimumMeshSizeOption("m", "minimum mesh size to consider", "0"); + parser.addOption(minimumMeshSizeOption); + if (!parser.parse(QCoreApplication::arguments())) { qCritical() << parser.errorText() << endl; @@ -138,13 +147,31 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : Q_UNREACHABLE(); } + int startMeshIndex = -1; + // check for an assignment pool passed on the command line or in the config + if (parser.isSet(startMeshIndexOption)) { + startMeshIndex = parser.value(startMeshIndexOption).toInt(); + } + + int endMeshIndex = -1; + // check for an assignment pool passed on the command line or in the config + if (parser.isSet(endMeshIndexOption)) { + endMeshIndex = parser.value(endMeshIndexOption).toInt(); + } + + float minimumMeshSize = 0.0f; + // check for an assignment pool passed on the command line or in the config + if (parser.isSet(minimumMeshSizeOption)) { + minimumMeshSize = parser.value(minimumMeshSizeOption).toFloat(); + } + //set parameters for V-HACD params.m_callback = &pCallBack; //progress callback params.m_resolution = 100000; // 100000 params.m_depth = 20; // 20 params.m_concavity = 0.001; // 0.001 - params.m_delta = 0.01; // 0.05 + params.m_delta = 0.05; // 0.05 params.m_planeDownsampling = 4; // 4 params.m_convexhullDownsampling = 4; // 4 params.m_alpha = 0.05; // 0.05 // controls the bias toward clipping along symmetry planes @@ -153,7 +180,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : params.m_pca = 0; // 0 enable/disable normalizing the mesh before applying the convex decomposition params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based params.m_maxNumVerticesPerCH = 64; // 64 - params.m_minVolumePerCH = 0.00001; // 0.0001 + params.m_minVolumePerCH = 0.0001; // 0.0001 params.m_callback = 0; // 0 params.m_logger = 0; // 0 params.m_convexhullApproximation = true; // true @@ -172,7 +199,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : begin = std::chrono::high_resolution_clock::now(); - if (!vUtil.computeVHACD(&fbx, params, &results)){ + if (!vUtil.computeVHACD(&fbx, params, &results, startMeshIndex, endMeshIndex, minimumMeshSize)) { cout << "Compute Failed..."; } end = std::chrono::high_resolution_clock::now(); From 7c4ada995344cd1fc7905a734f95828a1d973f40 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 26 Mar 2015 06:37:05 -0700 Subject: [PATCH 02/23] multiply points from fbs meshes by their transformation matrices --- tools/vhacd/src/VHACDUtil.cpp | 89 +++++++++++++++++++++++++++++++++-- tools/vhacd/src/VHACDUtil.h | 2 + 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp index c98a095f47..4f2cb0e2b8 100644 --- a/tools/vhacd/src/VHACDUtil.cpp +++ b/tools/vhacd/src/VHACDUtil.cpp @@ -44,7 +44,13 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re int count = 0; foreach(FBXMesh mesh, geometry.meshes) { //get vertices for each mesh - QVector vertices = mesh.vertices; + // QVector vertices = mesh.vertices; + + + QVector vertices; + foreach (glm::vec3 vertex, mesh.vertices) { + vertices.append(glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f))); + } //get the triangle indices for each mesh QVector triangles; @@ -73,9 +79,86 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re return true; } + +void vhacd::VHACDUtil::combineMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const { + float largestDimension = 0; + int indexStart = 0; + + QVector emptyVertices; + QVector emptyTriangles; + results->perMeshVertices.append(emptyVertices); + results->perMeshTriangleIndices.append(emptyTriangles); + results->perMeshLargestDimension.append(largestDimension); + + for (int i = 0; i < meshes->meshCount; i++) { + QVector vertices = meshes->perMeshVertices.at(i); + QVector triangles = meshes->perMeshTriangleIndices.at(i); + const float largestDimension = meshes->perMeshLargestDimension.at(i); + + for (int j = 0; j < triangles.size(); j++) { + triangles[ j ] += indexStart; + } + indexStart += vertices.size(); + + results->perMeshVertices[0] << vertices; + results->perMeshTriangleIndices[0] << triangles; + if (results->perMeshLargestDimension[0] < largestDimension) { + results->perMeshLargestDimension[0] = largestDimension; + } + } + + results->meshCount = 1; +} + + +void vhacd::VHACDUtil::fattenMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const { + + for (int i = 0; i < meshes->meshCount; i++) { + QVector vertices = meshes->perMeshVertices.at(i); + QVector triangles = meshes->perMeshTriangleIndices.at(i); + const float largestDimension = meshes->perMeshLargestDimension.at(i); + + results->perMeshVertices.append(vertices); + results->perMeshTriangleIndices.append(triangles); + results->perMeshLargestDimension.append(largestDimension); + + for (int j = 0; j < triangles.size(); j += 3) { + auto p0 = vertices[triangles[j]]; + auto p1 = vertices[triangles[j+1]]; + auto p2 = vertices[triangles[j+2]]; + + auto d0 = p1 - p0; + auto d1 = p2 - p0; + + auto cp = glm::cross(d0, d1); + cp = 5.0f * glm::normalize(cp); + + auto p3 = p0 + cp; + auto p4 = p1 + cp; + auto p5 = p2 + cp; + + auto n = results->perMeshVertices.size(); + results->perMeshVertices[i] << p3 << p4 << p5; + results->perMeshTriangleIndices[i] << n << n+1 << n+2; + } + + results->meshCount++; + } +} + + + bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results, int startMeshIndex, int endMeshIndex, float minimumMeshSize) const { + + // vhacd::LoadFBXResults *meshes = new vhacd::LoadFBXResults; + // combineMeshes(inMeshes, meshes); + + // vhacd::LoadFBXResults *meshes = new vhacd::LoadFBXResults; + // fattenMeshes(inMeshes, meshes); + + VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD(); int meshCount = meshes->meshCount; int count = 0; @@ -99,7 +182,7 @@ bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD qDebug() << "Mesh " << i << " -- " << nPoints << " points, " << nTriangles << " triangles, " << "size =" << largestDimension; - if (largestDimension < minimumMeshSize || largestDimension > 1000) { + if (largestDimension < minimumMeshSize /* || largestDimension > 1000 */) { qDebug() << " Skipping..."; continue; } @@ -135,8 +218,6 @@ bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD m_triangles_copy[ i ] = hull.m_triangles[ i ]; } hull.m_triangles = m_triangles_copy; - - convexHulls.append(hull); } results->convexHullList.append(convexHulls); diff --git a/tools/vhacd/src/VHACDUtil.h b/tools/vhacd/src/VHACDUtil.h index 304a7a2c5d..fd5d253334 100644 --- a/tools/vhacd/src/VHACDUtil.h +++ b/tools/vhacd/src/VHACDUtil.h @@ -40,6 +40,8 @@ namespace vhacd { class VHACDUtil { public: bool loadFBX(const QString filename, vhacd::LoadFBXResults *results); + void combineMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const; + void fattenMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const; bool computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results, int startMeshIndex, int endMeshIndex, float minimumMeshSize) const; ~VHACDUtil(); From 618992610534392d509307f086d7518e1f5a3e9f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 27 Mar 2015 13:23:29 -0700 Subject: [PATCH 03/23] add type hint to FST in model packager --- interface/src/Menu.h | 2 +- interface/src/ModelPackager.cpp | 24 +++++++++------- interface/src/ModelPropertiesDialog.cpp | 37 +++++++++++++++++++++++-- interface/src/ModelPropertiesDialog.h | 1 + interface/src/ModelSelector.cpp | 10 +++++-- interface/src/ui/ModelsBrowser.cpp | 2 +- interface/src/ui/ModelsBrowser.h | 3 +- interface/src/ui/PreferencesDialog.cpp | 2 +- libraries/fbx/src/FSTReader.cpp | 2 +- libraries/fbx/src/FSTReader.h | 1 + 10 files changed, 63 insertions(+), 21 deletions(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index b3d2d548df..508d684c3e 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -247,7 +247,7 @@ namespace MenuOption { const QString ToolWindow = "Tool Window"; const QString TransmitterDrive = "Transmitter Drive"; const QString TurnWithHead = "Turn using Head"; - const QString PackageModel = "Package Model"; + const QString PackageModel = "Package Model..."; const QString Visage = "Visage"; const QString Wireframe = "Wireframe"; } diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index f552d67a98..0cf1fdddc7 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -204,6 +204,17 @@ bool ModelPackager::zipModel() { } void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { + + + // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will + // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file + bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || + (geometry.blendshapeChannelNames.contains("BrowsDown_Right") && + geometry.blendshapeChannelNames.contains("MouthOpen") && + geometry.blendshapeChannelNames.contains("Blink_Left") && + geometry.blendshapeChannelNames.contains("Blink_Right") && + geometry.blendshapeChannelNames.contains("Squint_Right")); + if (!mapping.contains(NAME_FIELD)) { mapping.insert(NAME_FIELD, QFileInfo(filename).baseName()); } @@ -239,7 +250,7 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename joints.insert("jointLean", "Spine"); } if (!joints.contains("jointHead")) { - const char* topName = (geometry.applicationName == "mixamo.com") ? "HeadTop_End" : "HeadEnd"; + const char* topName = likelyMixamoFile ? "HeadTop_End" : "HeadEnd"; joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); } if (!joints.contains("jointLeftHand")) { @@ -256,15 +267,8 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); } - // mixamo blendshapes - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will - // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file - bool likelyMixamoFile = geometry.applicationName == "mixamo.com" || - (geometry.blendshapeChannelNames.contains("BrowsDown_Right") && - geometry.blendshapeChannelNames.contains("MouthOpen") && - geometry.blendshapeChannelNames.contains("Blink_Left") && - geometry.blendshapeChannelNames.contains("Blink_Right") && - geometry.blendshapeChannelNames.contains("Squint_Right")); - + // If there are no blendshape mappings, and we detect that this is likely a mixamo file, + // then we can add the default mixamo to "faceshift" mappings if (!mapping.contains(BLENDSHAPE_FIELD) && likelyMixamoFile) { QVariantHash blendshapes; blendshapes.insertMulti("BrowsD_L", QVariantList() << "BrowsDown_Left" << 1.0); diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index 81fe9ce7fd..c8c5669ce7 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -63,7 +63,7 @@ _geometry(geometry) form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); form->addRow("Neck Joint:", _neckJoint = createJointBox()); } - if (_modelType == SKELETON_MODEL) { + if (_modelType == BODY_ONLY_MODEL || _modelType == HEAD_AND_BODY_MODEL) { form->addRow("Root Joint:", _rootJoint = createJointBox()); form->addRow("Lean Joint:", _leanJoint = createJointBox()); form->addRow("Head Joint:", _headJoint = createJointBox()); @@ -89,8 +89,36 @@ _geometry(geometry) reset(); } + +QString ModelPropertiesDialog::getType() const { + QString type = "unknown"; + switch(_modelType) { + case ENTITY_MODEL: + type = "entity"; + break; + + case HEAD_MODEL: + type = "head"; + break; + + case BODY_ONLY_MODEL: + type = "body"; + break; + + case HEAD_AND_BODY_MODEL: + type = "body+head"; + break; + + case ATTACHMENT_MODEL: + type = "attachment"; + break; + } + return type; +} + QVariantHash ModelPropertiesDialog::getMapping() const { QVariantHash mapping = _originalMapping; + mapping.insert(TYPE_FIELD, getType()); mapping.insert(NAME_FIELD, _name->text()); mapping.insert(TEXDIR_FIELD, _textureDirectory->text()); mapping.insert(SCALE_FIELD, QString::number(_scale->value())); @@ -121,7 +149,9 @@ QVariantHash ModelPropertiesDialog::getMapping() const { insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText()); insertJointMapping(joints, "jointNeck", _neckJoint->currentText()); } - if (_modelType == SKELETON_MODEL) { + + + if (_modelType == BODY_ONLY_MODEL || _modelType == HEAD_AND_BODY_MODEL) { insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); insertJointMapping(joints, "jointLean", _leanJoint->currentText()); insertJointMapping(joints, "jointHead", _headJoint->currentText()); @@ -164,7 +194,8 @@ void ModelPropertiesDialog::reset() { setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString()); setJointText(_neckJoint, jointHash.value("jointNeck").toString()); } - if (_modelType == SKELETON_MODEL) { + + if (_modelType == BODY_ONLY_MODEL || _modelType == HEAD_AND_BODY_MODEL) { setJointText(_rootJoint, jointHash.value("jointRoot").toString()); setJointText(_leanJoint, jointHash.value("jointLean").toString()); setJointText(_headJoint, jointHash.value("jointHead").toString()); diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index 5af4d173f1..e2335acc23 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -43,6 +43,7 @@ private: QComboBox* createJointBox(bool withNone = true) const; QDoubleSpinBox* createTranslationBox() const; void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; + QString getType() const; ModelType _modelType; QVariantHash _originalMapping; diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index c55d77dc00..23746e0137 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -18,8 +18,9 @@ #include "ModelSelector.h" -static const QString AVATAR_HEAD_STRING = "Avatar Head"; -static const QString AVATAR_BODY_STRING = "Avatar Body"; +static const QString AVATAR_HEAD_STRING = "Avatar Head Only"; +static const QString AVATAR_BODY_STRING = "Avatar Body Only"; +static const QString AVATAR_HEAD_AND_BODY_STRING = "Avatar Body with Head"; static const QString AVATAR_ATTACHEMENT_STRING = "Avatar Attachment"; static const QString ENTITY_MODEL_STRING = "Entity Model"; @@ -36,6 +37,7 @@ ModelSelector::ModelSelector() { _modelType = new QComboBox(this); _modelType->addItem(AVATAR_HEAD_STRING); _modelType->addItem(AVATAR_BODY_STRING); + _modelType->addItem(AVATAR_HEAD_AND_BODY_STRING); _modelType->addItem(AVATAR_ATTACHEMENT_STRING); _modelType->addItem(ENTITY_MODEL_STRING); form->addRow("Model Type:", _modelType); @@ -56,7 +58,9 @@ ModelType ModelSelector::getModelType() const { if (text == AVATAR_HEAD_STRING) { return HEAD_MODEL; } else if (text == AVATAR_BODY_STRING) { - return SKELETON_MODEL; + return BODY_ONLY_MODEL; + } else if (text == AVATAR_HEAD_AND_BODY_STRING) { + return HEAD_AND_BODY_MODEL; } else if (text == AVATAR_ATTACHEMENT_STRING) { return ATTACHMENT_MODEL; } else if (text == ENTITY_MODEL_STRING) { diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 070c6a85cc..96c89b332d 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -28,7 +28,7 @@ #include "ModelsBrowser.h" -const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "attachments" }; +const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "skeletons", "attachments" }; static const QString S3_URL = "http://s3.amazonaws.com/hifi-public"; static const QString PUBLIC_URL = "http://public.highfidelity.io"; diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index 0c8bb59c85..207a7f8c4e 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -21,7 +21,8 @@ class QNetworkReply; enum ModelType { ENTITY_MODEL, HEAD_MODEL, - SKELETON_MODEL, + BODY_ONLY_MODEL, + HEAD_AND_BODY_MODEL, ATTACHMENT_MODEL }; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index a07de371a2..eddf009782 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -73,7 +73,7 @@ void PreferencesDialog::openHeadModelBrowser() { } void PreferencesDialog::openBodyModelBrowser() { - ModelsBrowser modelBrowser(SKELETON_MODEL); + ModelsBrowser modelBrowser(HEAD_AND_BODY_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl); modelBrowser.browse(); } diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index f1ffe11996..623f303c43 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -77,7 +77,7 @@ void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) { } QByteArray writeMapping(const QVariantHash& mapping) { - static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << SCALE_FIELD << FILENAME_FIELD + static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD << TEXDIR_FIELD << JOINT_FIELD << FREE_JOINT_FIELD << BLENDSHAPE_FIELD << JOINT_INDEX_FIELD; QBuffer buffer; diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 59559dea74..7f71403a28 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -15,6 +15,7 @@ #include static const QString NAME_FIELD = "name"; +static const QString TYPE_FIELD = "type"; static const QString FILENAME_FIELD = "filename"; static const QString TEXDIR_FIELD = "texdir"; static const QString LOD_FIELD = "lod"; From 42a1079322c36ea2d1c35a783d3c30e0acd78ebc Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 28 Mar 2015 00:27:57 +0100 Subject: [PATCH 04/23] limit 20 per sec. / don't delete on script stop --- examples/example/entities/makeHouses.js | 30 +++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/example/entities/makeHouses.js b/examples/example/entities/makeHouses.js index 37bc1d5a8e..cb78b1cf72 100644 --- a/examples/example/entities/makeHouses.js +++ b/examples/example/entities/makeHouses.js @@ -70,6 +70,7 @@ var rotOdd = Quat.fromPitchYawRollDegrees(0, 90.0 + MyAvatar.bodyYaw, 0.0); var housePos = Vec3.sum(MyAvatar.position, Quat.getFront(Camera.getOrientation())); + var housePositions = [] for (var j = 0; j < measures.rows; j++) { var posX1 = 0 - (xRange / 2); @@ -87,11 +88,8 @@ y: 0, z: dd }; - - print("House nr.:" + (houses.length + 1)); - houses.push( - addHouseAt(Vec3.sum(housePos, posShift), (j % 2 == 0) ? rotEven : rotOdd) - ); + + housePositions.push(Vec3.sum(housePos, posShift)); posX1 += measures.parcelWidth; } } @@ -144,14 +142,18 @@ }; } - function cleanup() { - while (houses.length > 0) { - if (!houses[0].isKnownID) { - houses[0] = Entities.identifyEntity(houses[0]); - } - Entities.deleteEntity(houses.shift()); + var addHouses = function() { + if (housePositions.length > 0) { + position = housePositions.pop(); + print("House nr.:" + (houses.length + 1)); + houses.push( + addHouseAt(position, (housePositions.length % 2 == 0) ? rotEven : rotOdd) + ); + + // max 20 per second + Script.setTimeout(addHouses, 50); } - } - - Script.scriptEnding.connect(cleanup); + }; + + addHouses(); })(); From baa2f947e5ddb7d42888718910a2299164d4d29b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 27 Mar 2015 16:30:00 -0700 Subject: [PATCH 05/23] classifying FSTReader and moving ModelType into that class --- interface/src/ModelPackager.cpp | 47 ++++++---- interface/src/ModelPackager.h | 2 +- interface/src/ModelPropertiesDialog.cpp | 44 +++------- interface/src/ModelPropertiesDialog.h | 5 +- interface/src/ModelSelector.cpp | 12 +-- interface/src/ModelSelector.h | 2 +- .../scripting/WindowScriptingInterface.cpp | 2 +- interface/src/ui/AttachmentsDialog.cpp | 2 +- interface/src/ui/ModelsBrowser.cpp | 4 +- interface/src/ui/ModelsBrowser.h | 16 ++-- interface/src/ui/PreferencesDialog.cpp | 4 +- libraries/fbx/src/FSTReader.cpp | 85 +++++++++++++++++-- libraries/fbx/src/FSTReader.h | 36 +++++++- libraries/render-utils/src/GeometryCache.cpp | 2 +- libraries/shared/src/StreamUtils.cpp | 8 ++ libraries/shared/src/StreamUtils.h | 2 + 16 files changed, 184 insertions(+), 89 deletions(-) diff --git a/interface/src/ModelPackager.cpp b/interface/src/ModelPackager.cpp index 0cf1fdddc7..a06dc05160 100644 --- a/interface/src/ModelPackager.cpp +++ b/interface/src/ModelPackager.cpp @@ -85,7 +85,7 @@ bool ModelPackager::loadModel() { return false; } qDebug() << "Reading FST file : " << _modelFile.filePath(); - _mapping = readMapping(fst.readAll()); + _mapping = FSTReader::readMapping(fst.readAll()); fst.close(); _fbxInfo = QFileInfo(_modelFile.path() + "/" + _mapping.value(FILENAME_FIELD).toString()); @@ -120,6 +120,7 @@ bool ModelPackager::editProperties() { } _mapping = properties.getMapping(); + /* // Make sure that a mapping for the root joint has been specified QVariantHash joints = _mapping.value(JOINT_FIELD).toHash(); if (!joints.contains("jointRoot")) { @@ -135,6 +136,7 @@ bool ModelPackager::editProperties() { return false; } + */ return true; } @@ -183,7 +185,7 @@ bool ModelPackager::zipModel() { // Copy FST QFile fst(tempDir.path() + "/" + nameField + ".fst"); if (fst.open(QIODevice::WriteOnly)) { - fst.write(writeMapping(_mapping)); + fst.write(FSTReader::writeMapping(_mapping)); fst.close(); } else { qDebug() << "Couldn't write FST file" << fst.fileName(); @@ -205,6 +207,7 @@ bool ModelPackager::zipModel() { void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename, const FBXGeometry& geometry) { + bool isBodyType = _modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL; // mixamo files - in the event that a mixamo file was edited by some other tool, it's likely the applicationName will // be rewritten, so we detect the existence of several different blendshapes which indicate we're likely a mixamo file @@ -243,28 +246,36 @@ void ModelPackager::populateBasicMapping(QVariantHash& mapping, QString filename if (!joints.contains("jointNeck")) { joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck"); } - if (!joints.contains("jointRoot")) { - joints.insert("jointRoot", "Hips"); - } - if (!joints.contains("jointLean")) { - joints.insert("jointLean", "Spine"); + + if (isBodyType) { + if (!joints.contains("jointRoot")) { + joints.insert("jointRoot", "Hips"); + } + if (!joints.contains("jointLean")) { + joints.insert("jointLean", "Spine"); + } + if (!joints.contains("jointLeftHand")) { + joints.insert("jointLeftHand", "LeftHand"); + } + if (!joints.contains("jointRightHand")) { + joints.insert("jointRightHand", "RightHand"); + } } + if (!joints.contains("jointHead")) { const char* topName = likelyMixamoFile ? "HeadTop_End" : "HeadEnd"; joints.insert("jointHead", geometry.jointIndices.contains(topName) ? topName : "Head"); } - if (!joints.contains("jointLeftHand")) { - joints.insert("jointLeftHand", "LeftHand"); - } - if (!joints.contains("jointRightHand")) { - joints.insert("jointRightHand", "RightHand"); - } + mapping.insert(JOINT_FIELD, joints); - if (!mapping.contains(FREE_JOINT_FIELD)) { - mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "RightArm"); - mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); + + if (isBodyType) { + if (!mapping.contains(FREE_JOINT_FIELD)) { + mapping.insertMulti(FREE_JOINT_FIELD, "LeftArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "LeftForeArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "RightArm"); + mapping.insertMulti(FREE_JOINT_FIELD, "RightForeArm"); + } } // If there are no blendshape mappings, and we detect that this is likely a mixamo file, diff --git a/interface/src/ModelPackager.h b/interface/src/ModelPackager.h index 2c90395e56..c681ae436f 100644 --- a/interface/src/ModelPackager.h +++ b/interface/src/ModelPackager.h @@ -35,7 +35,7 @@ private: QFileInfo _modelFile; QFileInfo _fbxInfo; - ModelType _modelType; + FSTReader::ModelType _modelType; QString _texDir; QVariantHash _mapping; diff --git a/interface/src/ModelPropertiesDialog.cpp b/interface/src/ModelPropertiesDialog.cpp index c8c5669ce7..28a50205c9 100644 --- a/interface/src/ModelPropertiesDialog.cpp +++ b/interface/src/ModelPropertiesDialog.cpp @@ -25,7 +25,7 @@ #include "ModelPropertiesDialog.h" -ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, +ModelPropertiesDialog::ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry) : _modelType(modelType), _originalMapping(originalMapping), @@ -46,8 +46,8 @@ _geometry(geometry) _scale->setMaximum(FLT_MAX); _scale->setSingleStep(0.01); - if (_modelType != ENTITY_MODEL) { - if (_modelType == ATTACHMENT_MODEL) { + if (_modelType != FSTReader::ENTITY_MODEL) { + if (_modelType == FSTReader::ATTACHMENT_MODEL) { QHBoxLayout* translation = new QHBoxLayout(); form->addRow("Translation:", translation); translation->addWidget(_translationX = createTranslationBox()); @@ -63,7 +63,7 @@ _geometry(geometry) form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox()); form->addRow("Neck Joint:", _neckJoint = createJointBox()); } - if (_modelType == BODY_ONLY_MODEL || _modelType == HEAD_AND_BODY_MODEL) { + if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { form->addRow("Root Joint:", _rootJoint = createJointBox()); form->addRow("Lean Joint:", _leanJoint = createJointBox()); form->addRow("Head Joint:", _headJoint = createJointBox()); @@ -91,29 +91,7 @@ _geometry(geometry) QString ModelPropertiesDialog::getType() const { - QString type = "unknown"; - switch(_modelType) { - case ENTITY_MODEL: - type = "entity"; - break; - - case HEAD_MODEL: - type = "head"; - break; - - case BODY_ONLY_MODEL: - type = "body"; - break; - - case HEAD_AND_BODY_MODEL: - type = "body+head"; - break; - - case ATTACHMENT_MODEL: - type = "attachment"; - break; - } - return type; + return FSTReader::getNameFromType(_modelType); } QVariantHash ModelPropertiesDialog::getMapping() const { @@ -130,9 +108,9 @@ QVariantHash ModelPropertiesDialog::getMapping() const { } mapping.insert(JOINT_INDEX_FIELD, jointIndices); - if (_modelType != ENTITY_MODEL) { + if (_modelType != FSTReader::ENTITY_MODEL) { QVariantHash joints = mapping.value(JOINT_FIELD).toHash(); - if (_modelType == ATTACHMENT_MODEL) { + if (_modelType == FSTReader::ATTACHMENT_MODEL) { glm::vec3 pivot; if (_pivotAboutCenter->isChecked()) { pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f; @@ -151,7 +129,7 @@ QVariantHash ModelPropertiesDialog::getMapping() const { } - if (_modelType == BODY_ONLY_MODEL || _modelType == HEAD_AND_BODY_MODEL) { + if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { insertJointMapping(joints, "jointRoot", _rootJoint->currentText()); insertJointMapping(joints, "jointLean", _leanJoint->currentText()); insertJointMapping(joints, "jointHead", _headJoint->currentText()); @@ -181,8 +159,8 @@ void ModelPropertiesDialog::reset() { QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash(); - if (_modelType != ENTITY_MODEL) { - if (_modelType == ATTACHMENT_MODEL) { + if (_modelType != FSTReader::ENTITY_MODEL) { + if (_modelType == FSTReader::ATTACHMENT_MODEL) { _translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble()); _translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble()); _translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble()); @@ -195,7 +173,7 @@ void ModelPropertiesDialog::reset() { setJointText(_neckJoint, jointHash.value("jointNeck").toString()); } - if (_modelType == BODY_ONLY_MODEL || _modelType == HEAD_AND_BODY_MODEL) { + if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) { setJointText(_rootJoint, jointHash.value("jointRoot").toString()); setJointText(_leanJoint, jointHash.value("jointLean").toString()); setJointText(_headJoint, jointHash.value("jointHead").toString()); diff --git a/interface/src/ModelPropertiesDialog.h b/interface/src/ModelPropertiesDialog.h index e2335acc23..11abc5ab54 100644 --- a/interface/src/ModelPropertiesDialog.h +++ b/interface/src/ModelPropertiesDialog.h @@ -15,6 +15,7 @@ #include #include +#include #include "ui/ModelsBrowser.h" @@ -28,7 +29,7 @@ class ModelPropertiesDialog : public QDialog { Q_OBJECT public: - ModelPropertiesDialog(ModelType modelType, const QVariantHash& originalMapping, + ModelPropertiesDialog(FSTReader::ModelType modelType, const QVariantHash& originalMapping, const QString& basePath, const FBXGeometry& geometry); QVariantHash getMapping() const; @@ -45,7 +46,7 @@ private: void insertJointMapping(QVariantHash& joints, const QString& joint, const QString& name) const; QString getType() const; - ModelType _modelType; + FSTReader::ModelType _modelType; QVariantHash _originalMapping; QString _basePath; FBXGeometry _geometry; diff --git a/interface/src/ModelSelector.cpp b/interface/src/ModelSelector.cpp index 23746e0137..8e130cec1a 100644 --- a/interface/src/ModelSelector.cpp +++ b/interface/src/ModelSelector.cpp @@ -52,19 +52,19 @@ QFileInfo ModelSelector::getFileInfo() const { return _modelFile; } -ModelType ModelSelector::getModelType() const { +FSTReader::ModelType ModelSelector::getModelType() const { QString text = _modelType->currentText(); if (text == AVATAR_HEAD_STRING) { - return HEAD_MODEL; + return FSTReader::HEAD_MODEL; } else if (text == AVATAR_BODY_STRING) { - return BODY_ONLY_MODEL; + return FSTReader::BODY_ONLY_MODEL; } else if (text == AVATAR_HEAD_AND_BODY_STRING) { - return HEAD_AND_BODY_MODEL; + return FSTReader::HEAD_AND_BODY_MODEL; } else if (text == AVATAR_ATTACHEMENT_STRING) { - return ATTACHMENT_MODEL; + return FSTReader::ATTACHMENT_MODEL; } else if (text == ENTITY_MODEL_STRING) { - return ENTITY_MODEL; + return FSTReader::ENTITY_MODEL; } else { Q_UNREACHABLE(); } diff --git a/interface/src/ModelSelector.h b/interface/src/ModelSelector.h index aaa35e01c3..0ac3df5963 100644 --- a/interface/src/ModelSelector.h +++ b/interface/src/ModelSelector.h @@ -29,7 +29,7 @@ public: ModelSelector(); QFileInfo getFileInfo() const; - ModelType getModelType() const; + FSTReader::ModelType getModelType() const; public slots: virtual void accept(); diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 7f4b5ddf45..6b55b645e8 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -637,7 +637,7 @@ QScriptValue WindowScriptingInterface::showBrowse(const QString& title, const QS /// \param const QString& nameFilter filter to filter filenames /// \return QScriptValue file path as a string if one was selected, otherwise `QScriptValue::NullValue` QScriptValue WindowScriptingInterface::showS3Browse(const QString& nameFilter) { - ModelsBrowser browser(ENTITY_MODEL); + ModelsBrowser browser(FSTReader::ENTITY_MODEL); if (nameFilter != "") { browser.setNameFilter(nameFilter); } diff --git a/interface/src/ui/AttachmentsDialog.cpp b/interface/src/ui/AttachmentsDialog.cpp index 58b46e5bbf..2a6ff2b2b1 100644 --- a/interface/src/ui/AttachmentsDialog.cpp +++ b/interface/src/ui/AttachmentsDialog.cpp @@ -164,7 +164,7 @@ AttachmentData AttachmentPanel::getAttachmentData() const { } void AttachmentPanel::chooseModelURL() { - ModelsBrowser modelBrowser(ATTACHMENT_MODEL, this); + ModelsBrowser modelBrowser(FSTReader::ATTACHMENT_MODEL, this); connect(&modelBrowser, SIGNAL(selected(QString)), SLOT(setModelURL(const QString&))); modelBrowser.browse(); } diff --git a/interface/src/ui/ModelsBrowser.cpp b/interface/src/ui/ModelsBrowser.cpp index 96c89b332d..91de4e36ac 100644 --- a/interface/src/ui/ModelsBrowser.cpp +++ b/interface/src/ui/ModelsBrowser.cpp @@ -71,7 +71,7 @@ static const QString propertiesIds[MODEL_METADATA_COUNT] = { "Tags" }; -ModelsBrowser::ModelsBrowser(ModelType modelsType, QWidget* parent) : +ModelsBrowser::ModelsBrowser(FSTReader::ModelType modelsType, QWidget* parent) : QWidget(parent, Qt::WindowStaysOnTopHint), _handler(new ModelHandler(modelsType)) { @@ -184,7 +184,7 @@ void ModelsBrowser::browse() { } -ModelHandler::ModelHandler(ModelType modelsType, QWidget* parent) : +ModelHandler::ModelHandler(FSTReader::ModelType modelsType, QWidget* parent) : QObject(parent), _initiateExit(false), _type(modelsType), diff --git a/interface/src/ui/ModelsBrowser.h b/interface/src/ui/ModelsBrowser.h index 207a7f8c4e..2cb3c67991 100644 --- a/interface/src/ui/ModelsBrowser.h +++ b/interface/src/ui/ModelsBrowser.h @@ -16,22 +16,16 @@ #include #include -class QNetworkReply; +#include -enum ModelType { - ENTITY_MODEL, - HEAD_MODEL, - BODY_ONLY_MODEL, - HEAD_AND_BODY_MODEL, - ATTACHMENT_MODEL -}; +class QNetworkReply; extern const char* MODEL_TYPE_NAMES[]; class ModelHandler : public QObject { Q_OBJECT public: - ModelHandler(ModelType modelsType, QWidget* parent = NULL); + ModelHandler(FSTReader::ModelType modelsType, QWidget* parent = NULL); void lockModel() { _lock.lockForRead(); } QStandardItemModel* getModel() { return &_model; } @@ -52,7 +46,7 @@ private slots: private: bool _initiateExit; - ModelType _type; + FSTReader::ModelType _type; QReadWriteLock _lock; QStandardItemModel _model; QString _nameFilter; @@ -67,7 +61,7 @@ class ModelsBrowser : public QWidget { Q_OBJECT public: - ModelsBrowser(ModelType modelsType, QWidget* parent = NULL); + ModelsBrowser(FSTReader::ModelType modelsType, QWidget* parent = NULL); QString getSelectedFile() { return _selectedFile; } signals: diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index eddf009782..4a9165ae44 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -67,13 +67,13 @@ void PreferencesDialog::setSkeletonUrl(QString modelUrl) { } void PreferencesDialog::openHeadModelBrowser() { - ModelsBrowser modelBrowser(HEAD_MODEL); + ModelsBrowser modelBrowser(FSTReader::HEAD_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl); modelBrowser.browse(); } void PreferencesDialog::openBodyModelBrowser() { - ModelsBrowser modelBrowser(HEAD_AND_BODY_MODEL); + ModelsBrowser modelBrowser(FSTReader::HEAD_AND_BODY_MODEL); connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl); modelBrowser.browse(); } diff --git a/libraries/fbx/src/FSTReader.cpp b/libraries/fbx/src/FSTReader.cpp index 623f303c43..0ae164ed5d 100644 --- a/libraries/fbx/src/FSTReader.cpp +++ b/libraries/fbx/src/FSTReader.cpp @@ -10,10 +10,11 @@ // #include +#include #include "FSTReader.h" -QVariantHash parseMapping(QIODevice* device) { +QVariantHash FSTReader::parseMapping(QIODevice* device) { QVariantHash properties; QByteArray line; @@ -48,13 +49,13 @@ QVariantHash parseMapping(QIODevice* device) { return properties; } -QVariantHash readMapping(const QByteArray& data) { +QVariantHash FSTReader::readMapping(const QByteArray& data) { QBuffer buffer(const_cast(&data)); buffer.open(QIODevice::ReadOnly); - return parseMapping(&buffer); + return FSTReader::parseMapping(&buffer); } -void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) { +void FSTReader::writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) { QByteArray key = it.key().toUtf8() + " = "; QVariantHash hashValue = it.value().toHash(); if (hashValue.isEmpty()) { @@ -76,7 +77,7 @@ void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it) { } } -QByteArray writeMapping(const QVariantHash& mapping) { +QByteArray FSTReader::writeMapping(const QVariantHash& mapping) { static const QStringList PREFERED_ORDER = QStringList() << NAME_FIELD << TYPE_FIELD << SCALE_FIELD << FILENAME_FIELD << TEXDIR_FIELD << JOINT_FIELD << FREE_JOINT_FIELD << BLENDSHAPE_FIELD << JOINT_INDEX_FIELD; @@ -96,4 +97,76 @@ QByteArray writeMapping(const QVariantHash& mapping) { } } return buffer.data(); -} \ No newline at end of file +} + +QHash FSTReader::_typesToNames; +QString FSTReader::getNameFromType(ModelType modelType) { + if (_typesToNames.size() == 0) { + _typesToNames[ENTITY_MODEL] = "entity"; + _typesToNames[HEAD_MODEL] = "head"; + _typesToNames[BODY_ONLY_MODEL] = "body"; + _typesToNames[HEAD_AND_BODY_MODEL] = "body+head"; + _typesToNames[ATTACHMENT_MODEL] = "attachment"; + } + return _typesToNames[modelType]; +} + +QHash FSTReader::_namesToTypes; +FSTReader::ModelType FSTReader::getTypeFromName(const QString& name) { + if (_namesToTypes.size() == 0) { + _namesToTypes["entity"] = ENTITY_MODEL; + _namesToTypes["head"] = HEAD_MODEL ; + _namesToTypes["body"] = BODY_ONLY_MODEL; + _namesToTypes["body+head"] = HEAD_AND_BODY_MODEL; + _namesToTypes["attachment"] = ATTACHMENT_MODEL; + } + return _namesToTypes[name]; +} + +FSTReader::ModelType FSTReader::predictModelType(const QVariantHash& mapping) { + + QVariantHash joints; + + if (mapping.contains("joint") && mapping["joint"].type() == QVariant::Hash) { + joints = mapping["joint"].toHash(); + } + + // if the mapping includes the type hint... then we trust the mapping + if (mapping.contains(TYPE_FIELD)) { + return FSTReader::getTypeFromName(mapping[TYPE_FIELD].toString()); + } + + // check for blendshapes + bool hasBlendshapes = mapping.contains(BLENDSHAPE_FIELD); + + // a Head needs to have these minimum fields... + //joint = jointEyeLeft = EyeL = 1 + //joint = jointEyeRight = EyeR = 1 + //joint = jointNeck = Head = 1 + bool hasHeadMinimum = joints.contains("jointNeck") && joints.contains("jointEyeLeft") && joints.contains("jointEyeRight"); + + // a Body needs to have these minimum fields... + //joint = jointRoot = Hips + //joint = jointLean = Spine + //joint = jointNeck = Neck + //joint = jointHead = HeadTop_End + + bool hasBodyMinimumJoints = joints.contains("jointRoot") && joints.contains("jointLean") && joints.contains("jointNeck") + && joints.contains("jointHead"); + + bool isLikelyHead = hasBlendshapes || hasHeadMinimum; + + if (isLikelyHead && hasBodyMinimumJoints) { + return HEAD_AND_BODY_MODEL; + } + + if (isLikelyHead) { + return HEAD_MODEL; + } + + if (hasBodyMinimumJoints) { + return BODY_ONLY_MODEL; + } + + return ENTITY_MODEL; +} diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 7f71403a28..463d9750c4 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -12,6 +12,7 @@ #ifndef hifi_FSTReader_h #define hifi_FSTReader_h +#include #include static const QString NAME_FIELD = "name"; @@ -28,10 +29,37 @@ static const QString JOINT_FIELD = "joint"; static const QString FREE_JOINT_FIELD = "freeJoint"; static const QString BLENDSHAPE_FIELD = "bs"; -/// Reads an FST mapping from the supplied data. -QVariantHash readMapping(const QByteArray& data); +class FSTReader { +public: -/// Writes an FST mapping to a byte array. -QByteArray writeMapping(const QVariantHash& mapping); + enum ModelType { + ENTITY_MODEL, + HEAD_MODEL, + BODY_ONLY_MODEL, + HEAD_AND_BODY_MODEL, + ATTACHMENT_MODEL + }; + + /// Reads an FST mapping from the supplied data. + static QVariantHash readMapping(const QByteArray& data); + + /// Writes an FST mapping to a byte array. + static QByteArray writeMapping(const QVariantHash& mapping); + + /// Predicts the type of model by examining the mapping + static ModelType predictModelType(const QVariantHash& mapping); + + static QString getTypeName(ModelType modelType); + + static QString getNameFromType(ModelType modelType); + static FSTReader::ModelType getTypeFromName(const QString& name); + +private: + static void writeVariant(QBuffer& buffer, QVariantHash::const_iterator& it); + static QVariantHash parseMapping(QIODevice* device); + + static QHash _typesToNames; + static QHash _namesToTypes; +}; #endif // hifi_FSTReader_h \ No newline at end of file diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index e60409e36f..71b73476fd 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2132,7 +2132,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { QUrl url = reply->url(); if (url.path().toLower().endsWith(".fst")) { // it's a mapping file; parse it and get the mesh filename - _mapping = readMapping(reply->readAll()); + _mapping = FSTReader::readMapping(reply->readAll()); reply->deleteLater(); QString filename = _mapping.value("filename").toString(); if (filename.isNull()) { diff --git a/libraries/shared/src/StreamUtils.cpp b/libraries/shared/src/StreamUtils.cpp index 96967648ed..f58115d288 100644 --- a/libraries/shared/src/StreamUtils.cpp +++ b/libraries/shared/src/StreamUtils.cpp @@ -143,4 +143,12 @@ QDebug& operator<<(QDebug& dbg, const glm::mat4& m) { return dbg << " ]}"; } +QDebug& operator<<(QDebug& dbg, const QVariantHash& v) { + dbg.nospace() << "["; + for (QVariantHash::const_iterator it = v.constBegin(); it != v.constEnd(); it++) { + dbg << it.key() << ":" << it.value(); + } + return dbg << " ]"; +} + #endif // QT_NO_DEBUG_STREAM diff --git a/libraries/shared/src/StreamUtils.h b/libraries/shared/src/StreamUtils.h index b9823a6743..fd22f7c068 100644 --- a/libraries/shared/src/StreamUtils.h +++ b/libraries/shared/src/StreamUtils.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -54,6 +55,7 @@ QDebug& operator<<(QDebug& s, const glm::vec3& v); QDebug& operator<<(QDebug& s, const glm::vec4& v); QDebug& operator<<(QDebug& s, const glm::quat& q); QDebug& operator<<(QDebug& s, const glm::mat4& m); +QDebug& operator<<(QDebug& dbg, const QVariantHash& v); #endif // QT_NO_DEBUG_STREAM #endif // hifi_StreamUtils_h From 463625f10159d3fa491af30dfe5f43229cd8ee49 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Sat, 28 Mar 2015 00:53:57 +0100 Subject: [PATCH 06/23] revert cleanup --- examples/example/entities/makeHouses.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/examples/example/entities/makeHouses.js b/examples/example/entities/makeHouses.js index cb78b1cf72..e31b3a7335 100644 --- a/examples/example/entities/makeHouses.js +++ b/examples/example/entities/makeHouses.js @@ -156,4 +156,16 @@ }; addHouses(); + + function cleanup() { + while (houses.length > 0) { + if (!houses[0].isKnownID) { + houses[0] = Entities.identifyEntity(houses[0]); + } + Entities.deleteEntity(houses.shift()); + Script.setTimeout(addHouses, 50); + } + } + + Script.scriptEnding.connect(cleanup); })(); From 002fba8018a026f63ca2577a74c068862976a18e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Fri, 27 Mar 2015 16:56:03 -0700 Subject: [PATCH 07/23] first cut at predicting FST type for set avatar --- interface/src/Application.cpp | 53 ++++++++++++++++++++++++++++++----- libraries/fbx/src/FSTReader.h | 2 -- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8cd36b501f..d50648e4d7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3656,17 +3656,56 @@ bool Application::askToSetAvatarUrl(const QString& url) { msgBox.exec(); return false; } - - QString message = "Would you like to use this model for part of avatar:\n" + url; + + // Download the FST file, to attempt to determine it's model type + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest = QNetworkRequest(url); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); + QNetworkReply* reply = networkAccessManager.get(networkRequest); + qDebug() << "Downloading avatar file at " << url; + QEventLoop loop; + QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + loop.exec(); + QByteArray fstContents = reply->readAll(); + delete reply; + QVariantHash fstMapping = FSTReader::readMapping(fstContents); + + FSTReader::ModelType modelType = FSTReader::predictModelType(fstMapping); + QMessageBox msgBox; - msgBox.setIcon(QMessageBox::Question); msgBox.setWindowTitle("Set Avatar"); - msgBox.setText(message); + QPushButton* headButton = NULL; + QPushButton* bodyButton = NULL; + QPushButton* bodyAndHeadButton = NULL; + + QString message; + QString typeInfo; + switch (modelType) { + case FSTReader::HEAD_MODEL: + message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar head?"); + headButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); + break; - QPushButton* headButton = msgBox.addButton(tr("Head"), QMessageBox::ActionRole); - QPushButton* bodyButton = msgBox.addButton(tr("Body"), QMessageBox::ActionRole); - QPushButton* bodyAndHeadButton = msgBox.addButton(tr("Body + Head"), QMessageBox::ActionRole); + case FSTReader::BODY_ONLY_MODEL: + message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar body?"); + bodyButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); + break; + + case FSTReader::HEAD_AND_BODY_MODEL: + message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for your avatar?"); + bodyAndHeadButton = msgBox.addButton(tr("Yes"), QMessageBox::ActionRole); + break; + + default: + message = QString("Would you like to use '") + fstMapping["name"].toString() + QString("' for some part of your avatar head?"); + headButton = msgBox.addButton(tr("Use for Head"), QMessageBox::ActionRole); + bodyButton = msgBox.addButton(tr("Use for Body"), QMessageBox::ActionRole); + bodyAndHeadButton = msgBox.addButton(tr("Use for Body and Head"), QMessageBox::ActionRole); + break; + } + + msgBox.setText(message); msgBox.addButton(QMessageBox::Cancel); msgBox.exec(); diff --git a/libraries/fbx/src/FSTReader.h b/libraries/fbx/src/FSTReader.h index 463d9750c4..5752a224c6 100644 --- a/libraries/fbx/src/FSTReader.h +++ b/libraries/fbx/src/FSTReader.h @@ -49,8 +49,6 @@ public: /// Predicts the type of model by examining the mapping static ModelType predictModelType(const QVariantHash& mapping); - static QString getTypeName(ModelType modelType); - static QString getNameFromType(ModelType modelType); static FSTReader::ModelType getTypeFromName(const QString& name); From 3bda65f4fe9a9cd3e17daa53fa686f8ea8f0f100 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 28 Mar 2015 19:19:11 -0700 Subject: [PATCH 08/23] if installing a body, and you have no head, make your head the default head --- interface/src/Application.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6e18c45468..6e4cd5c904 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3707,6 +3707,11 @@ bool Application::askToSetAvatarUrl(const QString& url) { } else if (msgBox.clickedButton() == bodyButton) { qDebug() << "Chose to use for body: " << url; _myAvatar->setSkeletonModelURL(url); + // if the head is empty, reset it to the default head. + if (_myAvatar->getFaceModelURLString().isEmpty()) { + _myAvatar->setFaceModelURL(DEFAULT_HEAD_MODEL_URL); + UserActivityLogger::getInstance().changedModel("head", DEFAULT_HEAD_MODEL_URL.toString()); + } UserActivityLogger::getInstance().changedModel("skeleton", url); _myAvatar->sendIdentityPacket(); } else if (msgBox.clickedButton() == bodyAndHeadButton) { From 72f1f5ba626e828929893e058434a146f62b1602 Mon Sep 17 00:00:00 2001 From: Stojce Slavkovski Date: Mon, 30 Mar 2015 22:08:26 +0200 Subject: [PATCH 09/23] stop makeHouses.js gracefully remove exit cleanup --- examples/example/entities/makeHouses.js | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/examples/example/entities/makeHouses.js b/examples/example/entities/makeHouses.js index e31b3a7335..d49f737880 100644 --- a/examples/example/entities/makeHouses.js +++ b/examples/example/entities/makeHouses.js @@ -152,20 +152,11 @@ // max 20 per second Script.setTimeout(addHouses, 50); + } else { + Script.stop(); } }; addHouses(); - function cleanup() { - while (houses.length > 0) { - if (!houses[0].isKnownID) { - houses[0] = Entities.identifyEntity(houses[0]); - } - Entities.deleteEntity(houses.shift()); - Script.setTimeout(addHouses, 50); - } - } - - Script.scriptEnding.connect(cleanup); })(); From 36657c94733008a3b7dfb09d6a0631363ef095da Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 30 Mar 2015 14:36:48 -0700 Subject: [PATCH 10/23] first cut at script caching --- interface/src/Application.cpp | 6 +- .../src/EntityTreeRenderer.cpp | 5 + libraries/script-engine/src/Script.cpp | 49 ++++++ libraries/script-engine/src/Script.h | 37 +++++ libraries/script-engine/src/ScriptCache.cpp | 146 ++++++++++++++++++ libraries/script-engine/src/ScriptCache.h | 46 ++++++ libraries/script-engine/src/ScriptEngine.cpp | 18 +++ libraries/script-engine/src/ScriptEngine.h | 6 +- 8 files changed, 309 insertions(+), 4 deletions(-) create mode 100644 libraries/script-engine/src/Script.cpp create mode 100644 libraries/script-engine/src/Script.h create mode 100644 libraries/script-engine/src/ScriptCache.cpp create mode 100644 libraries/script-engine/src/ScriptCache.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 97d132508a..c2ac472b9f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -76,7 +76,7 @@ #include #include #include -//#include +#include #include #include #include @@ -221,7 +221,7 @@ bool setupEssentials(int& argc, char** argv) { auto addressManager = DependencyManager::set(); auto nodeList = DependencyManager::set(NodeType::Agent, listenPort); auto geometryCache = DependencyManager::set(); - //auto scriptCache = DependencyManager::set(); + auto scriptCache = DependencyManager::set(); auto soundCache = DependencyManager::set(); auto glowEffect = DependencyManager::set(); auto faceshift = DependencyManager::set(); @@ -612,7 +612,7 @@ Application::~Application() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); - //DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); QThread* nodeThread = DependencyManager::get()->thread(); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3faa06fc53..f98a13878d 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -148,6 +148,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe qDebug() << "ERROR Loading file:" << fileName; } } else { + /* QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest = QNetworkRequest(url); networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -162,6 +163,10 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe qDebug() << "ERROR Loading file:" << url.toString(); } delete reply; + */ + auto scriptCache = DependencyManager::get(); + scriptContents = scriptCache->getScript(url); + } } diff --git a/libraries/script-engine/src/Script.cpp b/libraries/script-engine/src/Script.cpp new file mode 100644 index 0000000000..4f5d38a2e1 --- /dev/null +++ b/libraries/script-engine/src/Script.cpp @@ -0,0 +1,49 @@ +// +// Script.cpp +// libraries/script-engine/src +// +// Created by Brad Hefta-Gaub on 2015-03-30 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +/* +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "AudioRingBuffer.h" +#include "AudioFormat.h" +#include "AudioBuffer.h" +#include "AudioEditBuffer.h" +*/ + +#include "Script.h" + +Script::Script(const QUrl& url) : + Resource(url), + _isReady(false) +{ + +} + +void Script::downloadFinished(QNetworkReply* reply) { + // replace our byte array with the downloaded data + _contents = reply->readAll(); + qDebug() << "Script downloaded from:" << getURL(); + _isReady = true; + reply->deleteLater(); +} + diff --git a/libraries/script-engine/src/Script.h b/libraries/script-engine/src/Script.h new file mode 100644 index 0000000000..6cca76a8e8 --- /dev/null +++ b/libraries/script-engine/src/Script.h @@ -0,0 +1,37 @@ +// +// Script.h +// libraries/script-engine/src +// +// Created by Brad Hefta-Gaub on 2015-03-30 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_Script_h +#define hifi_Script_h + +#include +#include +#include + +#include + +class Script : public Resource { + Q_OBJECT +public: + Script(const QUrl& url); + bool isReady() const { return _isReady; } + const QString& getContents() { return _contents; } + +private: + QString _contents; + bool _isReady; + + virtual void downloadFinished(QNetworkReply* reply); +}; + +typedef QSharedPointer