Merge pull request #6702 from hyperlogic/tony/soft-attachments

Soft Attachment Support
This commit is contained in:
Brad Hefta-Gaub 2016-01-03 08:46:16 -10:00
commit 02835fc960
17 changed files with 271 additions and 57 deletions

View file

@ -2999,8 +2999,7 @@ void Application::update(float deltaTime) {
getEntities()->update(); // update the models...
}
myAvatar->harvestResultsFromPhysicsSimulation();
myAvatar->simulateAttachments(deltaTime);
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
}
}

View file

@ -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) {

View file

@ -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.

View file

@ -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;
}

View file

@ -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; }

View file

@ -0,0 +1,84 @@
//
// SoftAttachmentModel.cpp
// interface/src/avatar
//
// Created by Anthony J. Thibault on 12/17/15.
// Copyright 2013 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 "SoftAttachmentModel.h"
#include "InterfaceLogging.h"
SoftAttachmentModel::SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride) :
Model(rig, parent),
_rigOverride(rigOverride) {
assert(_rig);
assert(_rigOverride);
}
SoftAttachmentModel::~SoftAttachmentModel() {
}
// virtual
void SoftAttachmentModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
_needsUpdateClusterMatrices = true;
}
int SoftAttachmentModel::getJointIndexOverride(int i) const {
QString name = _rig->nameOfJoint(i);
if (name.isEmpty()) {
return -1;
}
return _rigOverride->indexOfJoint(name);
}
// virtual
// use the _rigOverride matrices instead of the Model::_rig
void SoftAttachmentModel::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
if (!_needsUpdateClusterMatrices) {
return;
}
_needsUpdateClusterMatrices = false;
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::mat4 modelToWorld = glm::mat4_cast(modelOrientation);
for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
// TODO: cache these look ups as an optimization
int jointIndexOverride = getJointIndexOverride(cluster.jointIndex);
glm::mat4 jointMatrix(glm::mat4::_null);
if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride->getJointStateCount()) {
jointMatrix = _rigOverride->getJointTransform(jointIndexOverride);
} else {
jointMatrix = _rig->getJointTransform(cluster.jointIndex);
}
state.clusterMatrices[j] = modelToWorld * jointMatrix * cluster.inverseBindMatrix;
}
// Once computed the cluster matrices, update the buffer(s)
if (mesh.clusters.size() > 1) {
if (!state.clusterBuffer) {
state.clusterBuffer = std::make_shared<gpu::Buffer>(state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
} else {
state.clusterBuffer->setSubData(0, state.clusterMatrices.size() * sizeof(glm::mat4),
(const gpu::Byte*) state.clusterMatrices.constData());
}
}
}
// post the blender if we're not currently waiting for one to finish
if (geometry.hasBlendedMeshes() && _blendshapeCoefficients != _blendedBlendshapeCoefficients) {
_blendedBlendshapeCoefficients = _blendshapeCoefficients;
DependencyManager::get<ModelBlender>()->noteRequiresBlend(this);
}
}

View file

@ -0,0 +1,42 @@
//
// SoftAttachmentModel.h
// interface/src/avatar
//
// Created by Anthony J. Thibault on 12/17/15.
// 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_SoftAttachmentModel_h
#define hifi_SoftAttachmentModel_h
#include <Model.h>
// A model that allows the creator to specify a secondary rig instance.
// When the cluster matrices are created for rendering, the
// cluster matrices will use the secondary rig for the joint poses
// instead of the primary rig.
//
// This is used by Avatar instances to wear clothing that follows the same
// animated pose as the SkeletonModel.
class SoftAttachmentModel : public Model {
Q_OBJECT
public:
SoftAttachmentModel(RigPointer rig, QObject* parent, RigPointer rigOverride);
~SoftAttachmentModel();
virtual void updateRig(float deltaTime, glm::mat4 parentTransform) override;
virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) override;
protected:
int getJointIndexOverride(int i) const;
RigPointer _rigOverride;
};
#endif // hifi_SoftAttachmentModel_h

View file

@ -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();
}

View file

@ -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;
};

View file

@ -245,6 +245,14 @@ int Rig::indexOfJoint(const QString& jointName) const {
}
}
QString Rig::nameOfJoint(int jointIndex) const {
if (_animSkeleton) {
return _animSkeleton->getJointName(jointIndex);
} else {
return "";
}
}
void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
AnimPose newModelOffset = AnimPose(modelOffsetMat);
if (!isEqual(_modelOffset.trans, newModelOffset.trans) ||

View file

@ -91,6 +91,7 @@ public:
bool jointStatesEmpty();
int getJointStateCount() const;
int indexOfJoint(const QString& jointName) const;
QString nameOfJoint(int jointIndex) const;
void setModelOffset(const glm::mat4& modelOffsetMat);

View file

@ -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,21 +1304,25 @@ 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 {
return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation &&
rotation == other.rotation && scale == other.scale;
rotation == other.rotation && scale == other.scale && isSoft == other.isSoft;
}
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 {

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -971,11 +971,14 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
_needsUpdateClusterMatrices = true;
_rig->updateAnimations(deltaTime, parentTransform);
}
void Model::simulateInternal(float deltaTime) {
// update the world space transforms for all joints
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
updateRig(deltaTime, parentTransform);
}
// virtual
void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
PerformanceTimer perfTimer("Model::updateClusterMatrices");

View file

@ -112,7 +112,7 @@ public:
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
virtual void simulate(float deltaTime, bool fullUpdate = true);
void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation);
virtual void updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation);
/// Returns a reference to the shared geometry.
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _geometry; }
@ -312,7 +312,7 @@ protected:
// hook for derived classes to be notified when setUrl invalidates the current model.
virtual void onInvalidate() {};
private:
protected:
void deleteGeometry();
void initJointTransforms();
@ -370,7 +370,6 @@ private:
bool _showCollisionHull = false;
friend class ModelMeshPartPayload;
protected:
RigPointer _rig;
};