mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 22:07:47 +02:00
merge upstream
This commit is contained in:
commit
52fc75a114
28 changed files with 508 additions and 155 deletions
|
@ -46,7 +46,7 @@ var diceButton = Overlays.addOverlay("image", {
|
|||
var GRAVITY = -3.5;
|
||||
var LIFETIME = 300;
|
||||
// NOTE: angularVelocity is in radians/sec
|
||||
var maxAngularSpeed = Math.PI;
|
||||
var MAX_ANGULAR_SPEED = Math.PI;
|
||||
|
||||
function shootDice(position, velocity) {
|
||||
for (var i = 0; i < NUMBER_OF_DICE; i++) {
|
||||
|
@ -56,7 +56,7 @@ function shootDice(position, velocity) {
|
|||
position: position,
|
||||
velocity: velocity,
|
||||
rotation: Quat.fromPitchYawRollDegrees(Math.random() * 360, Math.random() * 360, Math.random() * 360),
|
||||
angularVelocity: { x: Math.random() * maxAngularSpeed, y: Math.random() * maxAngularSpeed, z: Math.random() * maxAngularSpeed },
|
||||
angularVelocity: { x: Math.random() * MAX_ANGULAR_SPEED, y: Math.random() * MAX_ANGULAR_SPEED, z: Math.random() * MAX_ANGULAR_SPEED },
|
||||
lifetime: LIFETIME,
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
shapeType: "box",
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
#include <PhysicsEngine.h>
|
||||
#include <ProgramObject.h>
|
||||
#include <ResourceCache.h>
|
||||
//#include <ScriptCache.h>
|
||||
#include <ScriptCache.h>
|
||||
#include <SettingHandle.h>
|
||||
#include <SoundCache.h>
|
||||
#include <TextRenderer.h>
|
||||
|
@ -221,7 +221,7 @@ bool setupEssentials(int& argc, char** argv) {
|
|||
auto addressManager = DependencyManager::set<AddressManager>();
|
||||
auto nodeList = DependencyManager::set<NodeList>(NodeType::Agent, listenPort);
|
||||
auto geometryCache = DependencyManager::set<GeometryCache>();
|
||||
//auto scriptCache = DependencyManager::set<ScriptCache>();
|
||||
auto scriptCache = DependencyManager::set<ScriptCache>();
|
||||
auto soundCache = DependencyManager::set<SoundCache>();
|
||||
auto glowEffect = DependencyManager::set<GlowEffect>();
|
||||
auto faceshift = DependencyManager::set<Faceshift>();
|
||||
|
@ -608,6 +608,7 @@ Application::~Application() {
|
|||
|
||||
Menu::getInstance()->deleteLater();
|
||||
|
||||
_physicsEngine.setCharacterController(NULL);
|
||||
_myAvatar = NULL;
|
||||
|
||||
ModelEntityItem::cleanupLoadedAnimations();
|
||||
|
@ -619,7 +620,7 @@ Application::~Application() {
|
|||
DependencyManager::destroy<AnimationCache>();
|
||||
DependencyManager::destroy<TextureCache>();
|
||||
DependencyManager::destroy<GeometryCache>();
|
||||
//DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<ScriptCache>();
|
||||
DependencyManager::destroy<SoundCache>();
|
||||
|
||||
QThread* nodeThread = DependencyManager::get<NodeList>()->thread();
|
||||
|
@ -1523,7 +1524,7 @@ void Application::idle() {
|
|||
}
|
||||
|
||||
// Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing
|
||||
// details if we're in ExtraDebugging mode. However, the ::update() and its subcomponents will show their timing
|
||||
// details normally.
|
||||
bool showWarnings = getLogger()->extraDebugging();
|
||||
PerformanceWarning warn(showWarnings, "idle()");
|
||||
|
@ -2539,7 +2540,7 @@ bool Application::isHMDMode() const {
|
|||
QRect Application::getDesirableApplicationGeometry() {
|
||||
QRect applicationGeometry = getWindow()->geometry();
|
||||
|
||||
// If our parent window is on the HMD, then don't use it's geometry, instead use
|
||||
// If our parent window is on the HMD, then don't use its geometry, instead use
|
||||
// the "main screen" geometry.
|
||||
HMDToolsDialog* hmdTools = DependencyManager::get<DialogsManager>()->getHMDToolsDialog();
|
||||
if (hmdTools && hmdTools->hasHMDScreen()) {
|
||||
|
@ -3375,7 +3376,7 @@ void Application::nodeKilled(SharedNodePointer node) {
|
|||
|
||||
void Application::trackIncomingOctreePacket(const QByteArray& packet, const SharedNodePointer& sendingNode, bool wasStatsPacket) {
|
||||
|
||||
// Attempt to identify the sender from it's address.
|
||||
// Attempt to identify the sender from its address.
|
||||
if (sendingNode) {
|
||||
QUuid nodeUUID = sendingNode->getUUID();
|
||||
|
||||
|
@ -3444,7 +3445,7 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
|
|||
}
|
||||
// store jurisdiction details for later use
|
||||
// This is bit of fiddling is because JurisdictionMap assumes it is the owner of the values used to construct it
|
||||
// but OctreeSceneStats thinks it's just returning a reference to it's contents. So we need to make a copy of the
|
||||
// but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the
|
||||
// details from the OctreeSceneStats to construct the JurisdictionMap
|
||||
JurisdictionMap jurisdictionMap;
|
||||
jurisdictionMap.copyContents(temp.getJurisdictionRoot(), temp.getJurisdictionEndNodes());
|
||||
|
@ -3664,17 +3665,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 its 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();
|
||||
|
@ -3687,6 +3727,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) {
|
||||
|
|
|
@ -246,7 +246,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";
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
@ -119,21 +119,23 @@ bool ModelPackager::editProperties() {
|
|||
return false;
|
||||
}
|
||||
_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")) {
|
||||
qWarning() << QString("%1 root joint not configured for skeleton.").arg(_modelFile.fileName());
|
||||
|
||||
if (_modelType == FSTReader::BODY_ONLY_MODEL || _modelType == FSTReader::HEAD_AND_BODY_MODEL) {
|
||||
// Make sure that a mapping for the root joint has been specified
|
||||
QVariantHash joints = _mapping.value(JOINT_FIELD).toHash();
|
||||
if (!joints.contains("jointRoot")) {
|
||||
qWarning() << QString("%1 root joint not configured for skeleton.").arg(_modelFile.fileName());
|
||||
|
||||
QString message = "Your did not configure a root joint for your skeleton model.\n\nThe upload will be canceled.";
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Model Upload");
|
||||
msgBox.setText(message);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.exec();
|
||||
QString message = "Your did not configure a root joint for your skeleton model.\n\nPackaging will be canceled.";
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Model Packager");
|
||||
msgBox.setText(message);
|
||||
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||
msgBox.setIcon(QMessageBox::Warning);
|
||||
msgBox.exec();
|
||||
|
||||
return false;
|
||||
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();
|
||||
|
@ -204,6 +206,18 @@ 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
|
||||
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());
|
||||
}
|
||||
|
@ -232,39 +246,40 @@ 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 = (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")) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
|
|
@ -35,7 +35,7 @@ private:
|
|||
|
||||
QFileInfo _modelFile;
|
||||
QFileInfo _fbxInfo;
|
||||
ModelType _modelType;
|
||||
FSTReader::ModelType _modelType;
|
||||
QString _texDir;
|
||||
|
||||
QVariantHash _mapping;
|
||||
|
|
|
@ -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 == SKELETON_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());
|
||||
|
@ -89,8 +89,14 @@ _geometry(geometry)
|
|||
reset();
|
||||
}
|
||||
|
||||
|
||||
QString ModelPropertiesDialog::getType() const {
|
||||
return FSTReader::getNameFromType(_modelType);
|
||||
}
|
||||
|
||||
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()));
|
||||
|
@ -102,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;
|
||||
|
@ -121,7 +127,9 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
|
|||
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
|
||||
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
|
||||
}
|
||||
if (_modelType == SKELETON_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());
|
||||
|
@ -151,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());
|
||||
|
@ -164,7 +172,8 @@ void ModelPropertiesDialog::reset() {
|
|||
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
|
||||
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
|
||||
}
|
||||
if (_modelType == SKELETON_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());
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QDialog>
|
||||
|
||||
#include <FBXReader.h>
|
||||
#include <FSTReader.h>
|
||||
|
||||
#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;
|
||||
|
@ -43,8 +44,9 @@ 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;
|
||||
FSTReader::ModelType _modelType;
|
||||
QVariantHash _originalMapping;
|
||||
QString _basePath;
|
||||
FBXGeometry _geometry;
|
||||
|
|
|
@ -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);
|
||||
|
@ -50,17 +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 SKELETON_MODEL;
|
||||
return FSTReader::BODY_ONLY_MODEL;
|
||||
} else if (text == AVATAR_HEAD_AND_BODY_STRING) {
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
ModelSelector();
|
||||
|
||||
QFileInfo getFileInfo() const;
|
||||
ModelType getModelType() const;
|
||||
FSTReader::ModelType getModelType() const;
|
||||
|
||||
public slots:
|
||||
virtual void accept();
|
||||
|
|
|
@ -638,7 +638,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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
@ -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),
|
||||
|
|
|
@ -16,21 +16,16 @@
|
|||
#include <QStandardItemModel>
|
||||
#include <QTreeView>
|
||||
|
||||
class QNetworkReply;
|
||||
#include <FSTReader.h>
|
||||
|
||||
enum ModelType {
|
||||
ENTITY_MODEL,
|
||||
HEAD_MODEL,
|
||||
SKELETON_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; }
|
||||
|
@ -51,7 +46,7 @@ private slots:
|
|||
|
||||
private:
|
||||
bool _initiateExit;
|
||||
ModelType _type;
|
||||
FSTReader::ModelType _type;
|
||||
QReadWriteLock _lock;
|
||||
QStandardItemModel _model;
|
||||
QString _nameFilter;
|
||||
|
@ -66,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:
|
||||
|
|
|
@ -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(SKELETON_MODEL);
|
||||
ModelsBrowser modelBrowser(FSTReader::HEAD_AND_BODY_MODEL);
|
||||
connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl);
|
||||
modelBrowser.browse();
|
||||
}
|
||||
|
|
|
@ -110,14 +110,30 @@ void EntityTreeRenderer::shutdown() {
|
|||
_shuttingDown = true;
|
||||
}
|
||||
|
||||
void EntityTreeRenderer::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
|
||||
if (_waitingOnPreload.contains(url)) {
|
||||
QList<EntityItemID> entityIDs = _waitingOnPreload.values(url);
|
||||
_waitingOnPreload.remove(url);
|
||||
foreach(EntityItemID entityID, entityIDs) {
|
||||
checkAndCallPreload(entityID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID) {
|
||||
void EntityTreeRenderer::errorInLoadingScript(const QUrl& url) {
|
||||
if (_waitingOnPreload.contains(url)) {
|
||||
_waitingOnPreload.remove(url);
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(const EntityItemID& entityItemID, bool isPreload) {
|
||||
EntityItem* entity = static_cast<EntityTree*>(_tree)->findEntityByEntityItemID(entityItemID);
|
||||
return loadEntityScript(entity);
|
||||
return loadEntityScript(entity, isPreload);
|
||||
}
|
||||
|
||||
|
||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL) {
|
||||
QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& urlOut) {
|
||||
isPending = false;
|
||||
QUrl url(scriptMaybeURLorText);
|
||||
|
||||
// If the url is not valid, this must be script text...
|
||||
|
@ -126,6 +142,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
return scriptMaybeURLorText;
|
||||
}
|
||||
isURL = true;
|
||||
urlOut = url;
|
||||
|
||||
QString scriptContents; // assume empty
|
||||
|
||||
|
@ -148,20 +165,11 @@ 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);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
qDebug() << "Downloading script at" << url;
|
||||
QEventLoop loop;
|
||||
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
|
||||
loop.exec();
|
||||
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
|
||||
scriptContents = reply->readAll();
|
||||
} else {
|
||||
qDebug() << "ERROR Loading file:" << url.toString();
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
|
||||
if (!scriptCache->isInBadScriptList(url)) {
|
||||
scriptContents = scriptCache->getScript(url, this, isPending);
|
||||
}
|
||||
delete reply;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +177,7 @@ QString EntityTreeRenderer::loadScriptContents(const QString& scriptMaybeURLorTe
|
|||
}
|
||||
|
||||
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
||||
QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity, bool isPreload) {
|
||||
if (_shuttingDown) {
|
||||
return QScriptValue(); // since we're shutting down, we don't load any more scripts
|
||||
}
|
||||
|
@ -203,7 +211,24 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
|||
}
|
||||
|
||||
bool isURL = false; // loadScriptContents() will tell us if this is a URL or just text.
|
||||
QString scriptContents = loadScriptContents(entityScript, isURL);
|
||||
bool isPending = false;
|
||||
QUrl url;
|
||||
QString scriptContents = loadScriptContents(entityScript, isURL, isPending, url);
|
||||
|
||||
if (isPending && isPreload && isURL) {
|
||||
_waitingOnPreload.insert(url, entityID);
|
||||
|
||||
}
|
||||
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
|
||||
if (isURL && scriptCache->isInBadScriptList(url)) {
|
||||
return QScriptValue(); // no script contents...
|
||||
}
|
||||
|
||||
if (scriptContents.isEmpty()) {
|
||||
return QScriptValue(); // no script contents...
|
||||
}
|
||||
|
||||
QScriptSyntaxCheckResult syntaxCheck = QScriptEngine::checkSyntax(scriptContents);
|
||||
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
|
||||
|
@ -211,6 +236,9 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
|||
qDebug() << " " << syntaxCheck.errorMessage() << ":"
|
||||
<< syntaxCheck.errorLineNumber() << syntaxCheck.errorColumnNumber();
|
||||
qDebug() << " SCRIPT:" << entityScript;
|
||||
|
||||
scriptCache->addScriptToBadScriptList(url);
|
||||
|
||||
return QScriptValue(); // invalid script
|
||||
}
|
||||
|
||||
|
@ -223,6 +251,9 @@ QScriptValue EntityTreeRenderer::loadEntityScript(EntityItem* entity) {
|
|||
qDebug() << "EntityTreeRenderer::loadEntityScript() entity:" << entityID;
|
||||
qDebug() << " NOT CONSTRUCTOR";
|
||||
qDebug() << " SCRIPT:" << entityScript;
|
||||
|
||||
scriptCache->addScriptToBadScriptList(url);
|
||||
|
||||
return QScriptValue(); // invalid script
|
||||
} else {
|
||||
entityScriptConstructor = _entitiesScriptEngine->evaluate(scriptContents);
|
||||
|
@ -920,7 +951,7 @@ void EntityTreeRenderer::entitySciptChanging(const EntityItemID& entityID) {
|
|||
void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID) {
|
||||
if (_tree && !_shuttingDown) {
|
||||
// load the entity script if needed...
|
||||
QScriptValue entityScript = loadEntityScript(entityID);
|
||||
QScriptValue entityScript = loadEntityScript(entityID, true); // is preload!
|
||||
if (entityScript.property("preload").isValid()) {
|
||||
QScriptValueList entityArgs = createEntityArgs(entityID);
|
||||
entityScript.property("preload").call(entityScript, entityArgs);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <EntityScriptingInterface.h> // for RayToEntityIntersectionResult
|
||||
#include <MouseEvent.h>
|
||||
#include <OctreeRenderer.h>
|
||||
#include <ScriptCache.h>
|
||||
|
||||
class Model;
|
||||
class ScriptEngine;
|
||||
|
@ -31,7 +32,7 @@ public:
|
|||
};
|
||||
|
||||
// Generic client side Octree renderer class.
|
||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService {
|
||||
class EntityTreeRenderer : public OctreeRenderer, public EntityItemFBXService, public ScriptUser {
|
||||
Q_OBJECT
|
||||
public:
|
||||
EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
|
||||
|
@ -84,6 +85,9 @@ public:
|
|||
/// hovering over, and entering entities
|
||||
void connectSignalsToSlots(EntityScriptingInterface* entityScriptingInterface);
|
||||
|
||||
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents);
|
||||
virtual void errorInLoadingScript(const QUrl& url);
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
void mouseMoveOnEntity(const EntityItemID& entityItemID, const MouseEvent& event);
|
||||
|
@ -138,10 +142,10 @@ private:
|
|||
ScriptEngine* _entitiesScriptEngine;
|
||||
ScriptEngine* _sandboxScriptEngine;
|
||||
|
||||
QScriptValue loadEntityScript(EntityItem* entity);
|
||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID);
|
||||
QScriptValue loadEntityScript(EntityItem* entity, bool isPreload = false);
|
||||
QScriptValue loadEntityScript(const EntityItemID& entityItemID, bool isPreload = false);
|
||||
QScriptValue getPreviouslyLoadedEntityScript(const EntityItemID& entityItemID);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL);
|
||||
QString loadScriptContents(const QString& scriptMaybeURLorText, bool& isURL, bool& isPending, QUrl& url);
|
||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID);
|
||||
QScriptValueList createMouseEventArgs(const EntityItemID& entityID, const MouseEvent& mouseEvent);
|
||||
|
||||
|
@ -157,6 +161,8 @@ private:
|
|||
bool _dontDoPrecisionPicking;
|
||||
|
||||
bool _shuttingDown = false;
|
||||
|
||||
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include "FSTReader.h"
|
||||
|
||||
QVariantHash parseMapping(QIODevice* device) {
|
||||
QVariantHash FSTReader::parseMapping(QIODevice* device) {
|
||||
QVariantHash properties;
|
||||
|
||||
QByteArray line;
|
||||
|
@ -48,13 +48,13 @@ QVariantHash parseMapping(QIODevice* device) {
|
|||
return properties;
|
||||
}
|
||||
|
||||
QVariantHash readMapping(const QByteArray& data) {
|
||||
QVariantHash FSTReader::readMapping(const QByteArray& data) {
|
||||
QBuffer buffer(const_cast<QByteArray*>(&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,8 +76,8 @@ 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
|
||||
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;
|
||||
QBuffer buffer;
|
||||
|
@ -96,4 +96,76 @@ QByteArray writeMapping(const QVariantHash& mapping) {
|
|||
}
|
||||
}
|
||||
return buffer.data();
|
||||
}
|
||||
}
|
||||
|
||||
QHash<FSTReader::ModelType, QString> 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<QString, FSTReader::ModelType> 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;
|
||||
}
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
#ifndef hifi_FSTReader_h
|
||||
#define hifi_FSTReader_h
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QVariantHash>
|
||||
|
||||
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";
|
||||
|
@ -27,10 +29,35 @@ 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 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<FSTReader::ModelType, QString> _typesToNames;
|
||||
static QHash<QString, FSTReader::ModelType> _namesToTypes;
|
||||
};
|
||||
|
||||
#endif // hifi_FSTReader_h
|
|
@ -241,6 +241,12 @@ CharacterController::CharacterController(AvatarData* avatarData) {
|
|||
}
|
||||
|
||||
CharacterController::~CharacterController() {
|
||||
delete _ghostObject;
|
||||
_ghostObject = NULL;
|
||||
delete _convexShape;
|
||||
_convexShape = NULL;
|
||||
// make sure you remove this Character from its DynamicsWorld before reaching this spot
|
||||
assert(_dynamicsWorld == NULL);
|
||||
}
|
||||
|
||||
btPairCachingGhostObject* CharacterController::getGhostObject() {
|
||||
|
|
|
@ -28,6 +28,9 @@ PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
|
|||
}
|
||||
|
||||
PhysicsEngine::~PhysicsEngine() {
|
||||
if (_characterController) {
|
||||
_characterController->setDynamicsWorld(NULL);
|
||||
}
|
||||
// TODO: delete engine components... if we ever plan to create more than one instance
|
||||
delete _collisionConfig;
|
||||
delete _collisionDispatcher;
|
||||
|
@ -614,8 +617,14 @@ bool PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
|
|||
}
|
||||
|
||||
void PhysicsEngine::setCharacterController(CharacterController* character) {
|
||||
if (!_characterController) {
|
||||
if (_characterController != character) {
|
||||
lock();
|
||||
if (_characterController) {
|
||||
// remove the character from the DynamicsWorld immediately
|
||||
_characterController->setDynamicsWorld(NULL);
|
||||
_characterController = NULL;
|
||||
}
|
||||
// the character will be added to the DynamicsWorld later
|
||||
_characterController = character;
|
||||
unlock();
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -359,8 +359,12 @@ void Font::setupGL() {
|
|||
// FIXME there has to be a cleaner way of doing this
|
||||
QStringList Font::tokenizeForWrapping(const QString & str) const {
|
||||
QStringList result;
|
||||
foreach(const QString & token1, str.split(" ", QString::SkipEmptyParts)) {
|
||||
foreach(const QString & token1, str.split(" ")) {
|
||||
bool lineFeed = false;
|
||||
if (token1.isEmpty()) {
|
||||
result << token1;
|
||||
continue;
|
||||
}
|
||||
foreach(const QString & token2, token1.split("\n")) {
|
||||
if (lineFeed) {
|
||||
result << "\n";
|
||||
|
|
76
libraries/script-engine/src/ScriptCache.cpp
Normal file
76
libraries/script-engine/src/ScriptCache.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// ScriptCache.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 <QCoreApplication>
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QObject>
|
||||
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "ScriptCache.h"
|
||||
|
||||
ScriptCache::ScriptCache(QObject* parent) {
|
||||
// nothing to do here...
|
||||
}
|
||||
|
||||
QString ScriptCache::getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending) {
|
||||
QString scriptContents;
|
||||
if (_scriptCache.contains(url)) {
|
||||
qDebug() << "Found script in cache:" << url.toString();
|
||||
scriptContents = _scriptCache[url];
|
||||
scriptUser->scriptContentsAvailable(url, scriptContents);
|
||||
isPending = false;
|
||||
} else {
|
||||
isPending = true;
|
||||
bool alreadyWaiting = _scriptUsers.contains(url);
|
||||
_scriptUsers.insert(url, scriptUser);
|
||||
|
||||
if (alreadyWaiting) {
|
||||
qDebug() << "Already downloading script at:" << url.toString();
|
||||
} else {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
|
||||
qDebug() << "Downloading script at:" << url.toString();
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &ScriptCache::scriptDownloaded);
|
||||
}
|
||||
}
|
||||
return scriptContents;
|
||||
}
|
||||
|
||||
void ScriptCache::scriptDownloaded() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
QUrl url = reply->url();
|
||||
QList<ScriptUser*> scriptUsers = _scriptUsers.values(url);
|
||||
_scriptUsers.remove(url);
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
|
||||
_scriptCache[url] = reply->readAll();
|
||||
qDebug() << "Done downloading script at:" << url.toString();
|
||||
|
||||
foreach(ScriptUser* user, scriptUsers) {
|
||||
user->scriptContentsAvailable(url, _scriptCache[url]);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "ERROR Loading file:" << reply->url().toString();
|
||||
foreach(ScriptUser* user, scriptUsers) {
|
||||
user->errorInLoadingScript(url);
|
||||
}
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
|
44
libraries/script-engine/src/ScriptCache.h
Normal file
44
libraries/script-engine/src/ScriptCache.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// ScriptCache.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_ScriptCache_h
|
||||
#define hifi_ScriptCache_h
|
||||
|
||||
#include <ResourceCache.h>
|
||||
|
||||
class ScriptUser {
|
||||
public:
|
||||
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents) = 0;
|
||||
virtual void errorInLoadingScript(const QUrl& url) = 0;
|
||||
};
|
||||
|
||||
/// Interface for loading scripts
|
||||
class ScriptCache : public QObject, public Dependency {
|
||||
Q_OBJECT
|
||||
SINGLETON_DEPENDENCY
|
||||
|
||||
public:
|
||||
QString getScript(const QUrl& url, ScriptUser* scriptUser, bool& isPending);
|
||||
void addScriptToBadScriptList(const QUrl& url) { _badScripts.insert(url); }
|
||||
bool isInBadScriptList(const QUrl& url) { return _badScripts.contains(url); }
|
||||
|
||||
private slots:
|
||||
void scriptDownloaded();
|
||||
|
||||
private:
|
||||
ScriptCache(QObject* parent = NULL);
|
||||
|
||||
QHash<QUrl, QString> _scriptCache;
|
||||
QMultiMap<QUrl, ScriptUser*> _scriptUsers;
|
||||
QSet<QUrl> _badScripts;
|
||||
};
|
||||
|
||||
#endif // hifi_ScriptCache_h
|
|
@ -34,6 +34,7 @@
|
|||
#include "EventTypes.h"
|
||||
#include "MenuItemProperties.h"
|
||||
#include "ScriptAudioInjector.h"
|
||||
#include "ScriptCache.h"
|
||||
#include "ScriptEngine.h"
|
||||
#include "TypedArrays.h"
|
||||
#include "XMLHttpRequestClass.h"
|
||||
|
@ -275,31 +276,26 @@ void ScriptEngine::loadURL(const QUrl& scriptURL) {
|
|||
_scriptContents = in.readAll();
|
||||
emit scriptLoaded(_fileNameString);
|
||||
} else {
|
||||
qDebug() << "ERROR Loading file:" << _fileNameString;
|
||||
qDebug() << "ERROR Loading file:" << _fileNameString << "line:" << __LINE__;
|
||||
emit errorLoadingScript(_fileNameString);
|
||||
}
|
||||
} else {
|
||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||
QNetworkRequest networkRequest = QNetworkRequest(url);
|
||||
networkRequest.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
|
||||
QNetworkReply* reply = networkAccessManager.get(networkRequest);
|
||||
connect(reply, &QNetworkReply::finished, this, &ScriptEngine::handleScriptDownload);
|
||||
bool isPending;
|
||||
auto scriptCache = DependencyManager::get<ScriptCache>();
|
||||
scriptCache->getScript(url, this, isPending);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptEngine::handleScriptDownload() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 200) {
|
||||
_scriptContents = reply->readAll();
|
||||
emit scriptLoaded(_fileNameString);
|
||||
} else {
|
||||
qDebug() << "ERROR Loading file:" << reply->url().toString();
|
||||
emit errorLoadingScript(_fileNameString);
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
void ScriptEngine::scriptContentsAvailable(const QUrl& url, const QString& scriptContents) {
|
||||
_scriptContents = scriptContents;
|
||||
emit scriptLoaded(_fileNameString);
|
||||
}
|
||||
|
||||
void ScriptEngine::errorInLoadingScript(const QUrl& url) {
|
||||
qDebug() << "ERROR Loading file:" << url.toString() << "line:" << __LINE__;
|
||||
emit errorLoadingScript(_fileNameString); // ??
|
||||
}
|
||||
|
||||
void ScriptEngine::init() {
|
||||
|
@ -765,7 +761,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
|
|||
for (QUrl url : urls) {
|
||||
QString contents = data[url];
|
||||
if (contents.isNull()) {
|
||||
qDebug() << "Error loading file: " << url;
|
||||
qDebug() << "Error loading file: " << url << "line:" << __LINE__;
|
||||
} else {
|
||||
QScriptValue result = evaluate(contents, url.toString());
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "ArrayBufferClass.h"
|
||||
#include "AudioScriptingInterface.h"
|
||||
#include "Quat.h"
|
||||
#include "ScriptCache.h"
|
||||
#include "ScriptUUID.h"
|
||||
#include "Vec3.h"
|
||||
|
||||
|
@ -35,7 +36,7 @@ const QString NO_SCRIPT("");
|
|||
|
||||
const unsigned int SCRIPT_DATA_CALLBACK_USECS = floor(((1.0 / 60.0f) * 1000 * 1000) + 0.5);
|
||||
|
||||
class ScriptEngine : public QScriptEngine {
|
||||
class ScriptEngine : public QScriptEngine, public ScriptUser {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScriptEngine(const QString& scriptContents = NO_SCRIPT,
|
||||
|
@ -94,6 +95,9 @@ public:
|
|||
|
||||
void waitTillDoneRunning();
|
||||
|
||||
virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents);
|
||||
virtual void errorInLoadingScript(const QUrl& url);
|
||||
|
||||
public slots:
|
||||
void loadURL(const QUrl& scriptURL);
|
||||
void stop();
|
||||
|
@ -160,8 +164,6 @@ private:
|
|||
ArrayBufferClass* _arrayBufferClass;
|
||||
|
||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||
private slots:
|
||||
void handleScriptDownload();
|
||||
|
||||
private:
|
||||
static QSet<ScriptEngine*> _allKnownScriptEngines;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <iostream>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue