mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 14:03:55 +02:00
Network, Preferences and UI support for soft attachments
Added an isSoft field to the AttachmentData which is edited by the Attachment Dialog Menu, sent over the network via AvatarData identity packets and saved in the Interface.ini preferences. AvatarData and AvatarBulkData version number has been bumped. Changed Avatar attachment collections to use smart pointers to models instead of raw ones. Removed _unusedAttachmentModels. I don't think the caching was worth the added code complexity.
This commit is contained in:
parent
6b5b272cd7
commit
1618e0a92f
12 changed files with 132 additions and 54 deletions
|
@ -3017,8 +3017,7 @@ void Application::update(float deltaTime) {
|
|||
getEntities()->update(); // update the models...
|
||||
}
|
||||
|
||||
myAvatar->harvestResultsFromPhysicsSimulation();
|
||||
myAvatar->simulateAttachments(deltaTime);
|
||||
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "InterfaceLogging.h"
|
||||
#include "SoftAttachmentModel.h"
|
||||
#include <Rig.h>
|
||||
|
||||
using namespace std;
|
||||
|
@ -108,9 +109,6 @@ Avatar::Avatar(RigPointer rig) :
|
|||
|
||||
Avatar::~Avatar() {
|
||||
assert(_motionState == nullptr);
|
||||
for(auto attachment : _unusedAttachments) {
|
||||
delete attachment;
|
||||
}
|
||||
}
|
||||
|
||||
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
||||
|
@ -257,6 +255,8 @@ void Avatar::simulate(float deltaTime) {
|
|||
// until velocity is included in AvatarData update message.
|
||||
//_position += _velocity * deltaTime;
|
||||
measureMotionDerivatives(deltaTime);
|
||||
|
||||
simulateAttachments(deltaTime);
|
||||
}
|
||||
|
||||
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
|
||||
|
@ -324,7 +324,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
|
|||
_skeletonModel.addToScene(scene, pendingChanges);
|
||||
getHead()->getFaceModel().addToScene(scene, pendingChanges);
|
||||
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->addToScene(scene, pendingChanges);
|
||||
}
|
||||
|
||||
|
@ -335,7 +335,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
|||
pendingChanges.removeItem(_renderItemID);
|
||||
_skeletonModel.removeFromScene(scene, pendingChanges);
|
||||
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||
}
|
||||
}
|
||||
|
@ -565,15 +565,14 @@ void Avatar::fixupModelsInScene() {
|
|||
faceModel.removeFromScene(scene, pendingChanges);
|
||||
faceModel.addToScene(scene, pendingChanges);
|
||||
}
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||
attachmentModel->addToScene(scene, pendingChanges);
|
||||
}
|
||||
}
|
||||
for (auto attachmentModelToRemove : _attachmentsToRemove) {
|
||||
for (auto& attachmentModelToRemove : _attachmentsToRemove) {
|
||||
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
|
||||
_unusedAttachments << attachmentModelToRemove;
|
||||
}
|
||||
_attachmentsToRemove.clear();
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
|
@ -603,21 +602,29 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void Avatar::simulateAttachments(float deltaTime) {
|
||||
for (int i = 0; i < _attachmentModels.size(); i++) {
|
||||
const AttachmentData& attachment = _attachmentData.at(i);
|
||||
Model* model = _attachmentModels.at(i);
|
||||
auto& model = _attachmentModels.at(i);
|
||||
int jointIndex = getJointIndex(attachment.jointName);
|
||||
glm::vec3 jointPosition;
|
||||
glm::quat jointRotation;
|
||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale());
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setSnapModelToCenter(false); // hack to force resnap
|
||||
model->setSnapModelToCenter(true);
|
||||
if (attachment.isSoft) {
|
||||
// soft attachments do not have transform offsets
|
||||
model->setTranslation(getPosition());
|
||||
model->setRotation(getOrientation() * Quaternions::Y_180);
|
||||
model->simulate(deltaTime);
|
||||
} else {
|
||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale());
|
||||
model->setRotation(jointRotation * attachment.rotation);
|
||||
model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale
|
||||
model->setSnapModelToCenter(false); // hack to force resnap
|
||||
model->setSnapModelToCenter(true);
|
||||
model->simulate(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -940,13 +947,48 @@ void Avatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModel.setURL(_skeletonModelURL);
|
||||
}
|
||||
|
||||
// create new model, can return an instance of a SoftAttachmentModel rather then Model
|
||||
static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, RigPointer rigOverride) {
|
||||
if (isSoft) {
|
||||
// cast to std::shared_ptr<Model>
|
||||
return std::dynamic_pointer_cast<Model>(std::make_shared<SoftAttachmentModel>(std::make_shared<Rig>(), nullptr, rigOverride));
|
||||
} else {
|
||||
return std::make_shared<Model>(std::make_shared<Rig>());
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||
AvatarData::setAttachmentData(attachmentData);
|
||||
if (QThread::currentThread() != thread()) {
|
||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection,
|
||||
Q_ARG(const QVector<AttachmentData>, attachmentData));
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldAttachmentData = _attachmentData;
|
||||
AvatarData::setAttachmentData(attachmentData);
|
||||
|
||||
// if number of attachments has been reduced, remove excess models.
|
||||
while (_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
|
||||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
if (i == _attachmentModels.size()) {
|
||||
// if number of attachments has been increased, we need to allocate a new model
|
||||
_attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig()));
|
||||
}
|
||||
else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) {
|
||||
// if the attachment has changed type, we need to re-allocate a new one.
|
||||
_attachmentsToRemove.push_back(_attachmentModels[i]);
|
||||
_attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel.getRig());
|
||||
}
|
||||
_attachmentModels[i]->setURL(attachmentData[i].modelURL);
|
||||
}
|
||||
|
||||
// AJT: TODO REMOVE
|
||||
/*
|
||||
// make sure we have as many models as attachments
|
||||
while (_attachmentModels.size() < attachmentData.size()) {
|
||||
Model* model = nullptr;
|
||||
|
@ -959,16 +1001,20 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
|||
_attachmentModels.append(model);
|
||||
}
|
||||
while (_attachmentModels.size() > attachmentData.size()) {
|
||||
auto attachmentModel = _attachmentModels.takeLast();
|
||||
_attachmentsToRemove << attachmentModel;
|
||||
auto attachmentModel = _attachmentModels.back();
|
||||
_attachmentModels.pop_back();
|
||||
_attachmentsToRemove.push_back(attachmentModel);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// update the urls
|
||||
for (int i = 0; i < attachmentData.size(); i++) {
|
||||
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||
_attachmentModels[i]->setSnapModelToCenter(true);
|
||||
_attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void Avatar::setBillboard(const QByteArray& billboard) {
|
||||
|
|
|
@ -68,7 +68,7 @@ public:
|
|||
|
||||
void init();
|
||||
void simulate(float deltaTime);
|
||||
void simulateAttachments(float deltaTime);
|
||||
virtual void simulateAttachments(float deltaTime);
|
||||
|
||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||
|
||||
|
@ -177,9 +177,9 @@ protected:
|
|||
|
||||
SkeletonModel _skeletonModel;
|
||||
glm::vec3 _skeletonOffset;
|
||||
QVector<Model*> _attachmentModels;
|
||||
QVector<Model*> _attachmentsToRemove;
|
||||
QVector<Model*> _unusedAttachments;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||
|
||||
float _bodyYawDelta; // degrees/sec
|
||||
|
||||
// These position histories and derivatives are in the world-frame.
|
||||
|
|
|
@ -201,6 +201,11 @@ MyAvatar::~MyAvatar() {
|
|||
_lookAtTargetAvatar.reset();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void MyAvatar::simulateAttachments(float deltaTime) {
|
||||
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
|
||||
}
|
||||
|
||||
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||
CameraMode mode = qApp->getCamera()->getMode();
|
||||
_globalPosition = getPosition();
|
||||
|
@ -621,6 +626,7 @@ void MyAvatar::saveData() {
|
|||
settings.setValue("rotation_y", eulers.y);
|
||||
settings.setValue("rotation_z", eulers.z);
|
||||
settings.setValue("scale", attachment.scale);
|
||||
settings.setValue("isSoft", attachment.isSoft);
|
||||
}
|
||||
settings.endArray();
|
||||
|
||||
|
@ -702,6 +708,7 @@ void MyAvatar::loadData() {
|
|||
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
||||
attachment.rotation = glm::quat(eulers);
|
||||
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
||||
attachment.isSoft = settings.value("isSoft").toBool();
|
||||
attachmentData.append(attachment);
|
||||
}
|
||||
settings.endArray();
|
||||
|
@ -1057,7 +1064,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
|||
_characterController.setFollowVelocity(_followVelocity);
|
||||
}
|
||||
|
||||
void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
||||
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaType) {
|
||||
glm::vec3 position = getPosition();
|
||||
glm::quat orientation = getOrientation();
|
||||
_characterController.getPositionAndOrientation(position, orientation);
|
||||
|
@ -1068,6 +1075,9 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
|||
} else {
|
||||
setVelocity(_characterController.getLinearVelocity());
|
||||
}
|
||||
|
||||
// now that physics has adjusted our position, we can update attachements.
|
||||
Avatar::simulateAttachments(deltaType);
|
||||
}
|
||||
|
||||
void MyAvatar::adjustSensorTransform() {
|
||||
|
@ -1599,7 +1609,7 @@ void MyAvatar::maybeUpdateBillboard() {
|
|||
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
||||
return;
|
||||
}
|
||||
foreach (Model* model, _attachmentModels) {
|
||||
for (auto& model : _attachmentModels) {
|
||||
if (!model->isLoadedWithTextures()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ public:
|
|||
MyAvatar(RigPointer rig);
|
||||
~MyAvatar();
|
||||
|
||||
virtual void simulateAttachments(float deltaTime) override;
|
||||
|
||||
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
|
||||
AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; }
|
||||
AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; }
|
||||
|
@ -204,7 +206,7 @@ public:
|
|||
MyCharacterController* getCharacterController() { return &_characterController; }
|
||||
|
||||
void prepareForPhysicsSimulation();
|
||||
void harvestResultsFromPhysicsSimulation();
|
||||
void harvestResultsFromPhysicsSimulation(float deltaTime);
|
||||
void adjustSensorTransform();
|
||||
|
||||
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
//
|
||||
|
||||
#include "SoftAttachmentModel.h"
|
||||
#include "InterfaceLogging.h"
|
||||
|
||||
SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) :
|
||||
Model(rig, parent),
|
||||
|
@ -55,7 +56,7 @@ void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::qu
|
|||
// TODO: cache these look ups as an optimization
|
||||
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
|
||||
glm::mat4 jointMatrix(glm::mat4::_null);
|
||||
if (jointIndexOverride >= 0 && jointIndexOverride < getJointStateCount()) {
|
||||
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) {
|
||||
jointMatrix = _rigOverride->getJointTransform(jointIndexOverride);
|
||||
} else {
|
||||
jointMatrix = _rig->getJointTransform(cluster.jointIndex);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QScrollArea>
|
||||
#include <QVBoxLayout>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include <avatar/AvatarManager.h>
|
||||
#include <avatar/MyAvatar.h>
|
||||
|
@ -27,13 +28,13 @@
|
|||
|
||||
AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
||||
QDialog(parent) {
|
||||
|
||||
|
||||
setWindowTitle("Edit Attachments");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
setLayout(layout);
|
||||
|
||||
|
||||
QScrollArea* area = new QScrollArea();
|
||||
layout->addWidget(area);
|
||||
area->setWidgetResizable(true);
|
||||
|
@ -42,26 +43,26 @@ AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
|||
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||
area->setWidget(container);
|
||||
_attachments->addStretch(1);
|
||||
|
||||
|
||||
foreach (const AttachmentData& data, DependencyManager::get<AvatarManager>()->getMyAvatar()->getAttachmentData()) {
|
||||
addAttachment(data);
|
||||
}
|
||||
|
||||
|
||||
QPushButton* newAttachment = new QPushButton("New Attachment");
|
||||
connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
|
||||
layout->addWidget(newAttachment);
|
||||
|
||||
|
||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||
layout->addWidget(buttons);
|
||||
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
|
||||
_ok = buttons->button(QDialogButtonBox::Ok);
|
||||
|
||||
|
||||
setMinimumSize(600, 600);
|
||||
}
|
||||
|
||||
void AttachmentsDialog::setVisible(bool visible) {
|
||||
QDialog::setVisible(visible);
|
||||
|
||||
|
||||
// un-default the OK button
|
||||
if (visible) {
|
||||
_ok->setDefault(false);
|
||||
|
@ -104,11 +105,11 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
_dialog(dialog),
|
||||
_applying(false) {
|
||||
setFrameStyle(QFrame::StyledPanel);
|
||||
|
||||
|
||||
QFormLayout* layout = new QFormLayout();
|
||||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||
setLayout(layout);
|
||||
|
||||
|
||||
QHBoxLayout* urlBox = new QHBoxLayout();
|
||||
layout->addRow("Model URL:", urlBox);
|
||||
urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
|
||||
|
@ -117,7 +118,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
QPushButton* chooseURL = new QPushButton("Choose");
|
||||
urlBox->addWidget(chooseURL);
|
||||
connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
|
||||
|
||||
|
||||
layout->addRow("Joint:", _jointName = new QComboBox());
|
||||
QSharedPointer<NetworkGeometry> geometry = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSkeletonModel().getGeometry();
|
||||
if (geometry && geometry->isLoaded()) {
|
||||
|
@ -127,26 +128,30 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
|||
}
|
||||
_jointName->setCurrentText(data.jointName);
|
||||
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
|
||||
|
||||
|
||||
QHBoxLayout* translationBox = new QHBoxLayout();
|
||||
translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
|
||||
translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y));
|
||||
translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z));
|
||||
layout->addRow("Translation:", translationBox);
|
||||
|
||||
|
||||
QHBoxLayout* rotationBox = new QHBoxLayout();
|
||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
|
||||
rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x));
|
||||
rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y));
|
||||
rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z));
|
||||
layout->addRow("Rotation:", rotationBox);
|
||||
|
||||
|
||||
layout->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||
_scale->setSingleStep(0.01);
|
||||
_scale->setMaximum(FLT_MAX);
|
||||
_scale->setValue(data.scale);
|
||||
connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
||||
|
||||
|
||||
layout->addRow("Is Soft:", _isSoft = new QCheckBox());
|
||||
_isSoft->setChecked(data.isSoft);
|
||||
connect(_isSoft, SIGNAL(stateChanged(int)), SLOT(updateAttachmentData()));
|
||||
|
||||
QPushButton* remove = new QPushButton("Delete");
|
||||
layout->addRow(remove);
|
||||
connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
||||
|
@ -160,6 +165,7 @@ AttachmentData AttachmentPanel::getAttachmentData() const {
|
|||
data.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value());
|
||||
data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
|
||||
data.scale = _scale->value();
|
||||
data.isSoft = _isSoft->isChecked();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -227,6 +233,7 @@ void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) {
|
|||
_rotationY->setValue(eulers.y);
|
||||
_rotationZ->setValue(eulers.z);
|
||||
_scale->setValue(attachment.scale);
|
||||
_isSoft->setChecked(attachment.isSoft);
|
||||
_applying = false;
|
||||
_dialog->updateAttachmentData();
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ public slots:
|
|||
void updateAttachmentData();
|
||||
|
||||
private slots:
|
||||
|
||||
|
||||
void addAttachment(const AttachmentData& data = AttachmentData());
|
||||
|
||||
private:
|
||||
|
||||
|
||||
QVBoxLayout* _attachments;
|
||||
QPushButton* _ok;
|
||||
};
|
||||
|
@ -50,7 +50,7 @@ class AttachmentPanel : public QFrame {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
|
||||
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
||||
|
||||
AttachmentData getAttachmentData() const;
|
||||
|
@ -64,9 +64,9 @@ private slots:
|
|||
void updateAttachmentData();
|
||||
|
||||
private:
|
||||
|
||||
|
||||
void applyAttachmentData(const AttachmentData& attachment);
|
||||
|
||||
|
||||
AttachmentsDialog* _dialog;
|
||||
QLineEdit* _modelURL;
|
||||
QComboBox* _jointName;
|
||||
|
@ -77,6 +77,7 @@ private:
|
|||
QDoubleSpinBox* _rotationY;
|
||||
QDoubleSpinBox* _rotationZ;
|
||||
QDoubleSpinBox* _scale;
|
||||
QCheckBox* _isSoft;
|
||||
bool _applying;
|
||||
};
|
||||
|
||||
|
|
|
@ -1260,6 +1260,7 @@ void AvatarData::updateJointMappings() {
|
|||
static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl");
|
||||
static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName");
|
||||
static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform");
|
||||
static const QString JSON_ATTACHMENT_IS_SOFT = QStringLiteral("isSoft");
|
||||
|
||||
QJsonObject AttachmentData::toJson() const {
|
||||
QJsonObject result;
|
||||
|
@ -1278,6 +1279,7 @@ QJsonObject AttachmentData::toJson() const {
|
|||
if (!transform.isIdentity()) {
|
||||
result[JSON_ATTACHMENT_TRANSFORM] = Transform::toJson(transform);
|
||||
}
|
||||
result[JSON_ATTACHMENT_IS_SOFT] = isSoft;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1302,6 +1304,10 @@ void AttachmentData::fromJson(const QJsonObject& json) {
|
|||
rotation = transform.getRotation();
|
||||
scale = transform.getScale().x;
|
||||
}
|
||||
|
||||
if (json.contains(JSON_ATTACHMENT_IS_SOFT)) {
|
||||
isSoft = json[JSON_ATTACHMENT_IS_SOFT].toBool();
|
||||
}
|
||||
}
|
||||
|
||||
bool AttachmentData::operator==(const AttachmentData& other) const {
|
||||
|
@ -1311,12 +1317,12 @@ bool AttachmentData::operator==(const AttachmentData& other) const {
|
|||
|
||||
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) {
|
||||
return out << attachment.modelURL << attachment.jointName <<
|
||||
attachment.translation << attachment.rotation << attachment.scale;
|
||||
attachment.translation << attachment.rotation << attachment.scale << attachment.isSoft;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) {
|
||||
return in >> attachment.modelURL >> attachment.jointName >>
|
||||
attachment.translation >> attachment.rotation >> attachment.scale;
|
||||
attachment.translation >> attachment.rotation >> attachment.scale >> attachment.isSoft;
|
||||
}
|
||||
|
||||
void AttachmentDataObject::setModelURL(const QString& modelURL) const {
|
||||
|
|
|
@ -437,9 +437,10 @@ public:
|
|||
glm::vec3 translation;
|
||||
glm::quat rotation;
|
||||
float scale { 1.0f };
|
||||
|
||||
bool isSoft { false };
|
||||
|
||||
bool isValid() const { return modelURL.isValid(); }
|
||||
|
||||
|
||||
bool operator==(const AttachmentData& other) const;
|
||||
|
||||
QJsonObject toJson() const;
|
||||
|
|
|
@ -44,7 +44,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP;
|
||||
case PacketType::AvatarData:
|
||||
case PacketType::BulkAvatarData:
|
||||
return 17;
|
||||
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);
|
||||
default:
|
||||
return 17;
|
||||
}
|
||||
|
|
|
@ -163,4 +163,9 @@ const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50;
|
|||
const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51;
|
||||
const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
|
||||
|
||||
enum class AvatarMixerPacketVersion : PacketVersion {
|
||||
TranslationSupport = 17,
|
||||
SoftAttachmentSupport
|
||||
};
|
||||
|
||||
#endif // hifi_PacketHeaders_h
|
||||
|
|
Loading…
Reference in a new issue