diff --git a/examples/gun.js b/examples/gun.js index 4bfdb140b8..6cdf634239 100644 --- a/examples/gun.js +++ b/examples/gun.js @@ -39,6 +39,8 @@ var impactSound = new Sound("https://s3-us-west-1.amazonaws.com/highfidelity-pub var targetHitSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/hit.raw"); var targetLaunchSound = new Sound("http://highfidelity-public.s3-us-west-1.amazonaws.com/sounds/Space%20Invaders/shoot.raw"); +var gunModel = "http://highfidelity-public.s3-us-west-1.amazonaws.com/models/attachments/Raygun2.fst"; + var audioOptions = new AudioInjectionOptions(); audioOptions.volume = 0.9; @@ -190,6 +192,8 @@ function keyPressEvent(event) { } } +MyAvatar.attach(gunModel, "RightHand", {x: -0.10, y: 0.0, z: 0.0}, Quat.fromPitchYawRollDegrees(-90, 180, 0), 0.14); + function update(deltaTime) { // Check for mouseLook movement, update rotation // rotate body yaw for yaw received from mouse @@ -303,7 +307,8 @@ function mouseMoveEvent(event) { function scriptEnding() { Overlays.deleteOverlay(reticle); - Overlays.deleteOverlay(text); + Overlays.deleteOverlay(text); + MyAvatar.detachOne(gunModel); } Particles.particleCollisionWithVoxel.connect(particleCollisionWithVoxel); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index bc2bbb6712..bac339813f 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -709,7 +709,9 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { void Avatar::setAttachmentData(const QVector& attachmentData) { AvatarData::setAttachmentData(attachmentData); - + if (QThread::currentThread() != thread()) { + return; + } // make sure we have as many models as attachments while (_attachmentModels.size() < attachmentData.size()) { Model* model = new Model(this); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c3591862d1..7bc35ebd6d 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -584,6 +584,14 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _billboardValid = false; } +void MyAvatar::setAttachmentData(const QVector& attachmentData) { + Avatar::setAttachmentData(attachmentData); + if (QThread::currentThread() != thread()) { + return; + } + _billboardValid = false; +} + void MyAvatar::renderBody(RenderMode renderMode, float glowLevel) { if (!(_skeletonModel.isRenderable() && getHead()->getFaceModel().isRenderable())) { return; // wait until both models are loaded @@ -1247,6 +1255,11 @@ void MyAvatar::maybeUpdateBillboard() { if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) { return; } + foreach (Model* model, _attachmentModels) { + if (!model->isLoadedWithTextures()) { + return; + } + } QImage image = Application::getInstance()->renderAvatarBillboard(); _billboard.clear(); QBuffer buffer(&_billboard); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c07c27033c..885d56b61f 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -86,7 +86,8 @@ public: virtual void clearJointData(int index); virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); - + virtual void setAttachmentData(const QVector& attachmentData); + virtual void setCollisionGroups(quint32 collisionGroups); void setMotionBehaviorsByScript(quint32 flags); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ead5967c78..26613e51d0 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -661,16 +661,87 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); } -void AvatarData::setAttachmentData(const QVector& attachmentData) { - _attachmentData = attachmentData; -} - void AvatarData::setDisplayName(const QString& displayName) { _displayName = displayName; qDebug() << "Changing display name for avatar to" << displayName; } +QVector AvatarData::getAttachmentData() const { + if (QThread::currentThread() != thread()) { + QVector result; + QMetaObject::invokeMethod(const_cast(this), "getAttachmentData", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(QVector, result)); + return result; + } + return _attachmentData; +} + +void AvatarData::setAttachmentData(const QVector& attachmentData) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "setAttachmentData", Q_ARG(const QVector&, attachmentData)); + return; + } + _attachmentData = attachmentData; +} + +void AvatarData::attach(const QString& modelURL, const QString& jointName, const glm::vec3& translation, + const glm::quat& rotation, float scale, bool allowDuplicates) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "attach", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName), + Q_ARG(const glm::vec3&, translation), Q_ARG(const glm::quat&, rotation), + Q_ARG(float, scale), Q_ARG(bool, allowDuplicates)); + return; + } + QVector attachmentData = getAttachmentData(); + if (!allowDuplicates) { + foreach (const AttachmentData& data, attachmentData) { + if (data.modelURL == modelURL && (jointName.isEmpty() || data.jointName == jointName)) { + return; + } + } + } + AttachmentData data; + data.modelURL = modelURL; + data.jointName = jointName; + data.translation = translation; + data.rotation = rotation; + data.scale = scale; + attachmentData.append(data); + setAttachmentData(attachmentData); +} + +void AvatarData::detachOne(const QString& modelURL, const QString& jointName) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "detachOne", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); + return; + } + QVector attachmentData = getAttachmentData(); + for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); it++) { + if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { + attachmentData.erase(it); + setAttachmentData(attachmentData); + return; + } + } +} + +void AvatarData::detachAll(const QString& modelURL, const QString& jointName) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "detachAll", Q_ARG(const QString&, modelURL), Q_ARG(const QString&, jointName)); + return; + } + QVector attachmentData = getAttachmentData(); + for (QVector::iterator it = attachmentData.begin(); it != attachmentData.end(); ) { + if (it->modelURL == modelURL && (jointName.isEmpty() || it->jointName == jointName)) { + it = attachmentData.erase(it); + } else { + it++; + } + } + setAttachmentData(attachmentData); +} + void AvatarData::setBillboard(const QByteArray& billboard) { _billboard = billboard; @@ -793,3 +864,59 @@ QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) { attachment.translation >> attachment.rotation >> attachment.scale; } +void AttachmentDataObject::setModelURL(const QString& modelURL) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.modelURL = modelURL; + thisObject() = engine()->toScriptValue(data); +} + +QString AttachmentDataObject::getModelURL() const { + return qscriptvalue_cast(thisObject()).modelURL.toString(); +} + +void AttachmentDataObject::setJointName(const QString& jointName) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.jointName = jointName; + thisObject() = engine()->toScriptValue(data); +} + +QString AttachmentDataObject::getJointName() const { + return qscriptvalue_cast(thisObject()).jointName; +} + +void AttachmentDataObject::setTranslation(const glm::vec3& translation) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.translation = translation; + thisObject() = engine()->toScriptValue(data); +} + +glm::vec3 AttachmentDataObject::getTranslation() const { + return qscriptvalue_cast(thisObject()).translation; +} + +void AttachmentDataObject::setRotation(const glm::quat& rotation) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.rotation = rotation; + thisObject() = engine()->toScriptValue(data); +} + +glm::quat AttachmentDataObject::getRotation() const { + return qscriptvalue_cast(thisObject()).rotation; +} + +void AttachmentDataObject::setScale(float scale) const { + AttachmentData data = qscriptvalue_cast(thisObject()); + data.scale = scale; + thisObject() = engine()->toScriptValue(data); +} + +float AttachmentDataObject::getScale() const { + return qscriptvalue_cast(thisObject()).scale; +} + +void registerAvatarTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType >(engine); + engine->setDefaultPrototype(qMetaTypeId(), engine->newQObject( + new AttachmentDataObject(), QScriptEngine::ScriptOwnership)); +} + diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 50c1608be8..cc5ab16e35 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -41,6 +41,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include #include @@ -123,6 +124,7 @@ class AvatarData : public QObject { Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName) Q_PROPERTY(QString faceModelURL READ getFaceModelURLFromScript WRITE setFaceModelURLFromScript) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) + Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) Q_PROPERTY(QString billboardURL READ getBillboardURL WRITE setBillboardFromURL) Q_PROPERTY(QStringList jointNames READ getJointNames) @@ -229,13 +231,22 @@ public: const QUrl& getFaceModelURL() const { return _faceModelURL; } QString getFaceModelURLString() const { return _faceModelURL.toString(); } const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } - const QVector& getAttachmentData() const { return _attachmentData; } const QString& getDisplayName() const { return _displayName; } virtual void setFaceModelURL(const QUrl& faceModelURL); virtual void setSkeletonModelURL(const QUrl& skeletonModelURL); - virtual void setAttachmentData(const QVector& attachmentData); + virtual void setDisplayName(const QString& displayName); + Q_INVOKABLE QVector getAttachmentData() const; + Q_INVOKABLE virtual void setAttachmentData(const QVector& attachmentData); + + Q_INVOKABLE virtual void attach(const QString& modelURL, const QString& jointName = QString(), + const glm::vec3& translation = glm::vec3(), const glm::quat& rotation = glm::quat(), float scale = 1.0f, + bool allowDuplicates = false); + + Q_INVOKABLE void detachOne(const QString& modelURL, const QString& jointName = QString()); + Q_INVOKABLE void detachAll(const QString& modelURL, const QString& jointName = QString()); + virtual void setBillboard(const QByteArray& billboard); const QByteArray& getBillboard() const { return _billboard; } @@ -347,4 +358,36 @@ public: QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment); QDataStream& operator>>(QDataStream& in, AttachmentData& attachment); +Q_DECLARE_METATYPE(AttachmentData) +Q_DECLARE_METATYPE(QVector) + +/// Scriptable wrapper for attachments. +class AttachmentDataObject : public QObject, protected QScriptable { + Q_OBJECT + Q_PROPERTY(QString modelURL READ getModelURL WRITE setModelURL) + Q_PROPERTY(QString jointName READ getJointName WRITE setJointName) + Q_PROPERTY(glm::vec3 translation READ getTranslation WRITE setTranslation) + Q_PROPERTY(glm::quat rotation READ getRotation WRITE setRotation) + Q_PROPERTY(float scale READ getScale WRITE setScale) + +public: + + Q_INVOKABLE void setModelURL(const QString& modelURL) const; + Q_INVOKABLE QString getModelURL() const; + + Q_INVOKABLE void setJointName(const QString& jointName) const; + Q_INVOKABLE QString getJointName() const; + + Q_INVOKABLE void setTranslation(const glm::vec3& translation) const; + Q_INVOKABLE glm::vec3 getTranslation() const; + + Q_INVOKABLE void setRotation(const glm::quat& rotation) const; + Q_INVOKABLE glm::quat getRotation() const; + + Q_INVOKABLE void setScale(float scale) const; + Q_INVOKABLE float getScale() const; +}; + +void registerAvatarTypes(QScriptEngine* engine); + #endif // hifi_AvatarData_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 9be2cb5252..be97b37b46 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -210,6 +210,7 @@ void ScriptEngine::init() { registerEventTypes(&_engine); registerMenuItemProperties(&_engine); registerAnimationTypes(&_engine); + registerAvatarTypes(&_engine); qScriptRegisterMetaType(&_engine, ParticlePropertiesToScriptValue, ParticlePropertiesFromScriptValue); qScriptRegisterMetaType(&_engine, ParticleIDtoScriptValue, ParticleIDfromScriptValue);