mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 16:41:02 +02:00
Merge pull request #6702 from hyperlogic/tony/soft-attachments
Soft Attachment Support
This commit is contained in:
commit
02835fc960
17 changed files with 271 additions and 57 deletions
|
@ -2999,8 +2999,7 @@ void Application::update(float deltaTime) {
|
||||||
getEntities()->update(); // update the models...
|
getEntities()->update(); // update the models...
|
||||||
}
|
}
|
||||||
|
|
||||||
myAvatar->harvestResultsFromPhysicsSimulation();
|
myAvatar->harvestResultsFromPhysicsSimulation(deltaTime);
|
||||||
myAvatar->simulateAttachments(deltaTime);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
#include "world.h"
|
#include "world.h"
|
||||||
#include "InterfaceLogging.h"
|
#include "InterfaceLogging.h"
|
||||||
|
#include "SoftAttachmentModel.h"
|
||||||
#include <Rig.h>
|
#include <Rig.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -108,9 +109,6 @@ Avatar::Avatar(RigPointer rig) :
|
||||||
|
|
||||||
Avatar::~Avatar() {
|
Avatar::~Avatar() {
|
||||||
assert(_motionState == nullptr);
|
assert(_motionState == nullptr);
|
||||||
for(auto attachment : _unusedAttachments) {
|
|
||||||
delete attachment;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
const float BILLBOARD_LOD_DISTANCE = 40.0f;
|
||||||
|
@ -257,6 +255,8 @@ void Avatar::simulate(float deltaTime) {
|
||||||
// until velocity is included in AvatarData update message.
|
// until velocity is included in AvatarData update message.
|
||||||
//_position += _velocity * deltaTime;
|
//_position += _velocity * deltaTime;
|
||||||
measureMotionDerivatives(deltaTime);
|
measureMotionDerivatives(deltaTime);
|
||||||
|
|
||||||
|
simulateAttachments(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
|
bool Avatar::isLookingAtMe(AvatarSharedPointer avatar) const {
|
||||||
|
@ -324,7 +324,7 @@ bool Avatar::addToScene(AvatarSharedPointer self, std::shared_ptr<render::Scene>
|
||||||
_skeletonModel.addToScene(scene, pendingChanges);
|
_skeletonModel.addToScene(scene, pendingChanges);
|
||||||
getHead()->getFaceModel().addToScene(scene, pendingChanges);
|
getHead()->getFaceModel().addToScene(scene, pendingChanges);
|
||||||
|
|
||||||
for (auto attachmentModel : _attachmentModels) {
|
for (auto& attachmentModel : _attachmentModels) {
|
||||||
attachmentModel->addToScene(scene, pendingChanges);
|
attachmentModel->addToScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, std::shared_ptr<render::S
|
||||||
pendingChanges.removeItem(_renderItemID);
|
pendingChanges.removeItem(_renderItemID);
|
||||||
_skeletonModel.removeFromScene(scene, pendingChanges);
|
_skeletonModel.removeFromScene(scene, pendingChanges);
|
||||||
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
|
getHead()->getFaceModel().removeFromScene(scene, pendingChanges);
|
||||||
for (auto attachmentModel : _attachmentModels) {
|
for (auto& attachmentModel : _attachmentModels) {
|
||||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,15 +565,14 @@ void Avatar::fixupModelsInScene() {
|
||||||
faceModel.removeFromScene(scene, pendingChanges);
|
faceModel.removeFromScene(scene, pendingChanges);
|
||||||
faceModel.addToScene(scene, pendingChanges);
|
faceModel.addToScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
for (auto attachmentModel : _attachmentModels) {
|
for (auto& attachmentModel : _attachmentModels) {
|
||||||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||||
attachmentModel->removeFromScene(scene, pendingChanges);
|
attachmentModel->removeFromScene(scene, pendingChanges);
|
||||||
attachmentModel->addToScene(scene, pendingChanges);
|
attachmentModel->addToScene(scene, pendingChanges);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto attachmentModelToRemove : _attachmentsToRemove) {
|
for (auto& attachmentModelToRemove : _attachmentsToRemove) {
|
||||||
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
|
attachmentModelToRemove->removeFromScene(scene, pendingChanges);
|
||||||
_unusedAttachments << attachmentModelToRemove;
|
|
||||||
}
|
}
|
||||||
_attachmentsToRemove.clear();
|
_attachmentsToRemove.clear();
|
||||||
scene->enqueuePendingChanges(pendingChanges);
|
scene->enqueuePendingChanges(pendingChanges);
|
||||||
|
@ -603,21 +602,29 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
void Avatar::simulateAttachments(float deltaTime) {
|
void Avatar::simulateAttachments(float deltaTime) {
|
||||||
for (int i = 0; i < _attachmentModels.size(); i++) {
|
for (int i = 0; i < _attachmentModels.size(); i++) {
|
||||||
const AttachmentData& attachment = _attachmentData.at(i);
|
const AttachmentData& attachment = _attachmentData.at(i);
|
||||||
Model* model = _attachmentModels.at(i);
|
auto& model = _attachmentModels.at(i);
|
||||||
int jointIndex = getJointIndex(attachment.jointName);
|
int jointIndex = getJointIndex(attachment.jointName);
|
||||||
glm::vec3 jointPosition;
|
glm::vec3 jointPosition;
|
||||||
glm::quat jointRotation;
|
glm::quat jointRotation;
|
||||||
if (_skeletonModel.getJointPositionInWorldFrame(jointIndex, jointPosition) &&
|
if (attachment.isSoft) {
|
||||||
_skeletonModel.getJointRotationInWorldFrame(jointIndex, jointRotation)) {
|
// soft attachments do not have transform offsets
|
||||||
model->setTranslation(jointPosition + jointRotation * attachment.translation * getUniformScale());
|
model->setTranslation(getPosition());
|
||||||
model->setRotation(jointRotation * attachment.rotation);
|
model->setRotation(getOrientation() * Quaternions::Y_180);
|
||||||
model->setScaleToFit(true, getUniformScale() * attachment.scale, true); // hack to force rescale
|
|
||||||
model->setSnapModelToCenter(false); // hack to force resnap
|
|
||||||
model->setSnapModelToCenter(true);
|
|
||||||
model->simulate(deltaTime);
|
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);
|
_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) {
|
void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
AvatarData::setAttachmentData(attachmentData);
|
|
||||||
if (QThread::currentThread() != thread()) {
|
if (QThread::currentThread() != thread()) {
|
||||||
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection,
|
QMetaObject::invokeMethod(this, "setAttachmentData", Qt::DirectConnection,
|
||||||
Q_ARG(const QVector<AttachmentData>, attachmentData));
|
Q_ARG(const QVector<AttachmentData>, attachmentData));
|
||||||
return;
|
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
|
// make sure we have as many models as attachments
|
||||||
while (_attachmentModels.size() < attachmentData.size()) {
|
while (_attachmentModels.size() < attachmentData.size()) {
|
||||||
Model* model = nullptr;
|
Model* model = nullptr;
|
||||||
|
@ -959,16 +1001,20 @@ void Avatar::setAttachmentData(const QVector<AttachmentData>& attachmentData) {
|
||||||
_attachmentModels.append(model);
|
_attachmentModels.append(model);
|
||||||
}
|
}
|
||||||
while (_attachmentModels.size() > attachmentData.size()) {
|
while (_attachmentModels.size() > attachmentData.size()) {
|
||||||
auto attachmentModel = _attachmentModels.takeLast();
|
auto attachmentModel = _attachmentModels.back();
|
||||||
_attachmentsToRemove << attachmentModel;
|
_attachmentModels.pop_back();
|
||||||
|
_attachmentsToRemove.push_back(attachmentModel);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
// update the urls
|
// update the urls
|
||||||
for (int i = 0; i < attachmentData.size(); i++) {
|
for (int i = 0; i < attachmentData.size(); i++) {
|
||||||
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
_attachmentModels[i]->setURL(attachmentData.at(i).modelURL);
|
||||||
_attachmentModels[i]->setSnapModelToCenter(true);
|
_attachmentModels[i]->setSnapModelToCenter(true);
|
||||||
_attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale);
|
_attachmentModels[i]->setScaleToFit(true, getUniformScale() * _attachmentData.at(i).scale);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Avatar::setBillboard(const QByteArray& billboard) {
|
void Avatar::setBillboard(const QByteArray& billboard) {
|
||||||
|
|
|
@ -68,7 +68,7 @@ public:
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void simulateAttachments(float deltaTime);
|
virtual void simulateAttachments(float deltaTime);
|
||||||
|
|
||||||
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
virtual void render(RenderArgs* renderArgs, const glm::vec3& cameraPosition);
|
||||||
|
|
||||||
|
@ -177,9 +177,9 @@ protected:
|
||||||
|
|
||||||
SkeletonModel _skeletonModel;
|
SkeletonModel _skeletonModel;
|
||||||
glm::vec3 _skeletonOffset;
|
glm::vec3 _skeletonOffset;
|
||||||
QVector<Model*> _attachmentModels;
|
std::vector<std::shared_ptr<Model>> _attachmentModels;
|
||||||
QVector<Model*> _attachmentsToRemove;
|
std::vector<std::shared_ptr<Model>> _attachmentsToRemove;
|
||||||
QVector<Model*> _unusedAttachments;
|
|
||||||
float _bodyYawDelta; // degrees/sec
|
float _bodyYawDelta; // degrees/sec
|
||||||
|
|
||||||
// These position histories and derivatives are in the world-frame.
|
// These position histories and derivatives are in the world-frame.
|
||||||
|
|
|
@ -201,6 +201,11 @@ MyAvatar::~MyAvatar() {
|
||||||
_lookAtTargetAvatar.reset();
|
_lookAtTargetAvatar.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
|
void MyAvatar::simulateAttachments(float deltaTime) {
|
||||||
|
// don't update attachments here, do it in harvestResultsFromPhysicsSimulation()
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
|
QByteArray MyAvatar::toByteArray(bool cullSmallChanges, bool sendAll) {
|
||||||
CameraMode mode = qApp->getCamera()->getMode();
|
CameraMode mode = qApp->getCamera()->getMode();
|
||||||
_globalPosition = getPosition();
|
_globalPosition = getPosition();
|
||||||
|
@ -621,6 +626,7 @@ void MyAvatar::saveData() {
|
||||||
settings.setValue("rotation_y", eulers.y);
|
settings.setValue("rotation_y", eulers.y);
|
||||||
settings.setValue("rotation_z", eulers.z);
|
settings.setValue("rotation_z", eulers.z);
|
||||||
settings.setValue("scale", attachment.scale);
|
settings.setValue("scale", attachment.scale);
|
||||||
|
settings.setValue("isSoft", attachment.isSoft);
|
||||||
}
|
}
|
||||||
settings.endArray();
|
settings.endArray();
|
||||||
|
|
||||||
|
@ -702,6 +708,7 @@ void MyAvatar::loadData() {
|
||||||
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
eulers.z = loadSetting(settings, "rotation_z", 0.0f);
|
||||||
attachment.rotation = glm::quat(eulers);
|
attachment.rotation = glm::quat(eulers);
|
||||||
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
attachment.scale = loadSetting(settings, "scale", 1.0f);
|
||||||
|
attachment.isSoft = settings.value("isSoft").toBool();
|
||||||
attachmentData.append(attachment);
|
attachmentData.append(attachment);
|
||||||
}
|
}
|
||||||
settings.endArray();
|
settings.endArray();
|
||||||
|
@ -1057,7 +1064,7 @@ void MyAvatar::prepareForPhysicsSimulation() {
|
||||||
_characterController.setFollowVelocity(_followVelocity);
|
_characterController.setFollowVelocity(_followVelocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
void MyAvatar::harvestResultsFromPhysicsSimulation(float deltaType) {
|
||||||
glm::vec3 position = getPosition();
|
glm::vec3 position = getPosition();
|
||||||
glm::quat orientation = getOrientation();
|
glm::quat orientation = getOrientation();
|
||||||
_characterController.getPositionAndOrientation(position, orientation);
|
_characterController.getPositionAndOrientation(position, orientation);
|
||||||
|
@ -1068,6 +1075,9 @@ void MyAvatar::harvestResultsFromPhysicsSimulation() {
|
||||||
} else {
|
} else {
|
||||||
setVelocity(_characterController.getLinearVelocity());
|
setVelocity(_characterController.getLinearVelocity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now that physics has adjusted our position, we can update attachements.
|
||||||
|
Avatar::simulateAttachments(deltaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::adjustSensorTransform() {
|
void MyAvatar::adjustSensorTransform() {
|
||||||
|
@ -1599,7 +1609,7 @@ void MyAvatar::maybeUpdateBillboard() {
|
||||||
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
if (_billboardValid || !(_skeletonModel.isLoadedWithTextures() && getHead()->getFaceModel().isLoadedWithTextures())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
foreach (Model* model, _attachmentModels) {
|
for (auto& model : _attachmentModels) {
|
||||||
if (!model->isLoadedWithTextures()) {
|
if (!model->isLoadedWithTextures()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,6 +83,8 @@ public:
|
||||||
MyAvatar(RigPointer rig);
|
MyAvatar(RigPointer rig);
|
||||||
~MyAvatar();
|
~MyAvatar();
|
||||||
|
|
||||||
|
virtual void simulateAttachments(float deltaTime) override;
|
||||||
|
|
||||||
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
|
AudioListenerMode getAudioListenerModeHead() const { return FROM_HEAD; }
|
||||||
AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; }
|
AudioListenerMode getAudioListenerModeCamera() const { return FROM_CAMERA; }
|
||||||
AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; }
|
AudioListenerMode getAudioListenerModeCustom() const { return CUSTOM; }
|
||||||
|
@ -204,7 +206,7 @@ public:
|
||||||
MyCharacterController* getCharacterController() { return &_characterController; }
|
MyCharacterController* getCharacterController() { return &_characterController; }
|
||||||
|
|
||||||
void prepareForPhysicsSimulation();
|
void prepareForPhysicsSimulation();
|
||||||
void harvestResultsFromPhysicsSimulation();
|
void harvestResultsFromPhysicsSimulation(float deltaTime);
|
||||||
void adjustSensorTransform();
|
void adjustSensorTransform();
|
||||||
|
|
||||||
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
|
const QString& getCollisionSoundURL() { return _collisionSoundURL; }
|
||||||
|
|
84
interface/src/avatar/SoftAttachmentModel.cpp
Normal file
84
interface/src/avatar/SoftAttachmentModel.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
42
interface/src/avatar/SoftAttachmentModel.h
Normal file
42
interface/src/avatar/SoftAttachmentModel.h
Normal 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
|
|
@ -17,6 +17,7 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QScrollArea>
|
#include <QScrollArea>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QCheckBox>
|
||||||
|
|
||||||
#include <avatar/AvatarManager.h>
|
#include <avatar/AvatarManager.h>
|
||||||
#include <avatar/MyAvatar.h>
|
#include <avatar/MyAvatar.h>
|
||||||
|
@ -27,13 +28,13 @@
|
||||||
|
|
||||||
AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
||||||
QDialog(parent) {
|
QDialog(parent) {
|
||||||
|
|
||||||
setWindowTitle("Edit Attachments");
|
setWindowTitle("Edit Attachments");
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
QScrollArea* area = new QScrollArea();
|
QScrollArea* area = new QScrollArea();
|
||||||
layout->addWidget(area);
|
layout->addWidget(area);
|
||||||
area->setWidgetResizable(true);
|
area->setWidgetResizable(true);
|
||||||
|
@ -42,26 +43,26 @@ AttachmentsDialog::AttachmentsDialog(QWidget* parent) :
|
||||||
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
container->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||||
area->setWidget(container);
|
area->setWidget(container);
|
||||||
_attachments->addStretch(1);
|
_attachments->addStretch(1);
|
||||||
|
|
||||||
foreach (const AttachmentData& data, DependencyManager::get<AvatarManager>()->getMyAvatar()->getAttachmentData()) {
|
foreach (const AttachmentData& data, DependencyManager::get<AvatarManager>()->getMyAvatar()->getAttachmentData()) {
|
||||||
addAttachment(data);
|
addAttachment(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton* newAttachment = new QPushButton("New Attachment");
|
QPushButton* newAttachment = new QPushButton("New Attachment");
|
||||||
connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
|
connect(newAttachment, SIGNAL(clicked(bool)), SLOT(addAttachment()));
|
||||||
layout->addWidget(newAttachment);
|
layout->addWidget(newAttachment);
|
||||||
|
|
||||||
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok);
|
||||||
layout->addWidget(buttons);
|
layout->addWidget(buttons);
|
||||||
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
|
connect(buttons, SIGNAL(accepted()), SLOT(deleteLater()));
|
||||||
_ok = buttons->button(QDialogButtonBox::Ok);
|
_ok = buttons->button(QDialogButtonBox::Ok);
|
||||||
|
|
||||||
setMinimumSize(600, 600);
|
setMinimumSize(600, 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AttachmentsDialog::setVisible(bool visible) {
|
void AttachmentsDialog::setVisible(bool visible) {
|
||||||
QDialog::setVisible(visible);
|
QDialog::setVisible(visible);
|
||||||
|
|
||||||
// un-default the OK button
|
// un-default the OK button
|
||||||
if (visible) {
|
if (visible) {
|
||||||
_ok->setDefault(false);
|
_ok->setDefault(false);
|
||||||
|
@ -104,11 +105,11 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
||||||
_dialog(dialog),
|
_dialog(dialog),
|
||||||
_applying(false) {
|
_applying(false) {
|
||||||
setFrameStyle(QFrame::StyledPanel);
|
setFrameStyle(QFrame::StyledPanel);
|
||||||
|
|
||||||
QFormLayout* layout = new QFormLayout();
|
QFormLayout* layout = new QFormLayout();
|
||||||
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
QHBoxLayout* urlBox = new QHBoxLayout();
|
QHBoxLayout* urlBox = new QHBoxLayout();
|
||||||
layout->addRow("Model URL:", urlBox);
|
layout->addRow("Model URL:", urlBox);
|
||||||
urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
|
urlBox->addWidget(_modelURL = new QLineEdit(data.modelURL.toString()), 1);
|
||||||
|
@ -117,7 +118,7 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
||||||
QPushButton* chooseURL = new QPushButton("Choose");
|
QPushButton* chooseURL = new QPushButton("Choose");
|
||||||
urlBox->addWidget(chooseURL);
|
urlBox->addWidget(chooseURL);
|
||||||
connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
|
connect(chooseURL, SIGNAL(clicked(bool)), SLOT(chooseModelURL()));
|
||||||
|
|
||||||
layout->addRow("Joint:", _jointName = new QComboBox());
|
layout->addRow("Joint:", _jointName = new QComboBox());
|
||||||
QSharedPointer<NetworkGeometry> geometry = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSkeletonModel().getGeometry();
|
QSharedPointer<NetworkGeometry> geometry = DependencyManager::get<AvatarManager>()->getMyAvatar()->getSkeletonModel().getGeometry();
|
||||||
if (geometry && geometry->isLoaded()) {
|
if (geometry && geometry->isLoaded()) {
|
||||||
|
@ -127,26 +128,30 @@ AttachmentPanel::AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData
|
||||||
}
|
}
|
||||||
_jointName->setCurrentText(data.jointName);
|
_jointName->setCurrentText(data.jointName);
|
||||||
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
|
connect(_jointName, SIGNAL(currentIndexChanged(int)), SLOT(jointNameChanged()));
|
||||||
|
|
||||||
QHBoxLayout* translationBox = new QHBoxLayout();
|
QHBoxLayout* translationBox = new QHBoxLayout();
|
||||||
translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
|
translationBox->addWidget(_translationX = createTranslationBox(this, data.translation.x));
|
||||||
translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y));
|
translationBox->addWidget(_translationY = createTranslationBox(this, data.translation.y));
|
||||||
translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z));
|
translationBox->addWidget(_translationZ = createTranslationBox(this, data.translation.z));
|
||||||
layout->addRow("Translation:", translationBox);
|
layout->addRow("Translation:", translationBox);
|
||||||
|
|
||||||
QHBoxLayout* rotationBox = new QHBoxLayout();
|
QHBoxLayout* rotationBox = new QHBoxLayout();
|
||||||
glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
|
glm::vec3 eulers = glm::degrees(safeEulerAngles(data.rotation));
|
||||||
rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x));
|
rotationBox->addWidget(_rotationX = createRotationBox(this, eulers.x));
|
||||||
rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y));
|
rotationBox->addWidget(_rotationY = createRotationBox(this, eulers.y));
|
||||||
rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z));
|
rotationBox->addWidget(_rotationZ = createRotationBox(this, eulers.z));
|
||||||
layout->addRow("Rotation:", rotationBox);
|
layout->addRow("Rotation:", rotationBox);
|
||||||
|
|
||||||
layout->addRow("Scale:", _scale = new QDoubleSpinBox());
|
layout->addRow("Scale:", _scale = new QDoubleSpinBox());
|
||||||
_scale->setSingleStep(0.01);
|
_scale->setSingleStep(0.01);
|
||||||
_scale->setMaximum(FLT_MAX);
|
_scale->setMaximum(FLT_MAX);
|
||||||
_scale->setValue(data.scale);
|
_scale->setValue(data.scale);
|
||||||
connect(_scale, SIGNAL(valueChanged(double)), SLOT(updateAttachmentData()));
|
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");
|
QPushButton* remove = new QPushButton("Delete");
|
||||||
layout->addRow(remove);
|
layout->addRow(remove);
|
||||||
connect(remove, SIGNAL(clicked(bool)), SLOT(deleteLater()));
|
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.translation = glm::vec3(_translationX->value(), _translationY->value(), _translationZ->value());
|
||||||
data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
|
data.rotation = glm::quat(glm::radians(glm::vec3(_rotationX->value(), _rotationY->value(), _rotationZ->value())));
|
||||||
data.scale = _scale->value();
|
data.scale = _scale->value();
|
||||||
|
data.isSoft = _isSoft->isChecked();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,6 +233,7 @@ void AttachmentPanel::applyAttachmentData(const AttachmentData& attachment) {
|
||||||
_rotationY->setValue(eulers.y);
|
_rotationY->setValue(eulers.y);
|
||||||
_rotationZ->setValue(eulers.z);
|
_rotationZ->setValue(eulers.z);
|
||||||
_scale->setValue(attachment.scale);
|
_scale->setValue(attachment.scale);
|
||||||
|
_isSoft->setChecked(attachment.isSoft);
|
||||||
_applying = false;
|
_applying = false;
|
||||||
_dialog->updateAttachmentData();
|
_dialog->updateAttachmentData();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,11 @@ public slots:
|
||||||
void updateAttachmentData();
|
void updateAttachmentData();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void addAttachment(const AttachmentData& data = AttachmentData());
|
void addAttachment(const AttachmentData& data = AttachmentData());
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QVBoxLayout* _attachments;
|
QVBoxLayout* _attachments;
|
||||||
QPushButton* _ok;
|
QPushButton* _ok;
|
||||||
};
|
};
|
||||||
|
@ -50,7 +50,7 @@ class AttachmentPanel : public QFrame {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
AttachmentPanel(AttachmentsDialog* dialog, const AttachmentData& data = AttachmentData());
|
||||||
|
|
||||||
AttachmentData getAttachmentData() const;
|
AttachmentData getAttachmentData() const;
|
||||||
|
@ -64,9 +64,9 @@ private slots:
|
||||||
void updateAttachmentData();
|
void updateAttachmentData();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void applyAttachmentData(const AttachmentData& attachment);
|
void applyAttachmentData(const AttachmentData& attachment);
|
||||||
|
|
||||||
AttachmentsDialog* _dialog;
|
AttachmentsDialog* _dialog;
|
||||||
QLineEdit* _modelURL;
|
QLineEdit* _modelURL;
|
||||||
QComboBox* _jointName;
|
QComboBox* _jointName;
|
||||||
|
@ -77,6 +77,7 @@ private:
|
||||||
QDoubleSpinBox* _rotationY;
|
QDoubleSpinBox* _rotationY;
|
||||||
QDoubleSpinBox* _rotationZ;
|
QDoubleSpinBox* _rotationZ;
|
||||||
QDoubleSpinBox* _scale;
|
QDoubleSpinBox* _scale;
|
||||||
|
QCheckBox* _isSoft;
|
||||||
bool _applying;
|
bool _applying;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
void Rig::setModelOffset(const glm::mat4& modelOffsetMat) {
|
||||||
AnimPose newModelOffset = AnimPose(modelOffsetMat);
|
AnimPose newModelOffset = AnimPose(modelOffsetMat);
|
||||||
if (!isEqual(_modelOffset.trans, newModelOffset.trans) ||
|
if (!isEqual(_modelOffset.trans, newModelOffset.trans) ||
|
||||||
|
|
|
@ -91,6 +91,7 @@ public:
|
||||||
bool jointStatesEmpty();
|
bool jointStatesEmpty();
|
||||||
int getJointStateCount() const;
|
int getJointStateCount() const;
|
||||||
int indexOfJoint(const QString& jointName) const;
|
int indexOfJoint(const QString& jointName) const;
|
||||||
|
QString nameOfJoint(int jointIndex) const;
|
||||||
|
|
||||||
void setModelOffset(const glm::mat4& modelOffsetMat);
|
void setModelOffset(const glm::mat4& modelOffsetMat);
|
||||||
|
|
||||||
|
|
|
@ -1260,6 +1260,7 @@ void AvatarData::updateJointMappings() {
|
||||||
static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl");
|
static const QString JSON_ATTACHMENT_URL = QStringLiteral("modelUrl");
|
||||||
static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName");
|
static const QString JSON_ATTACHMENT_JOINT_NAME = QStringLiteral("jointName");
|
||||||
static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform");
|
static const QString JSON_ATTACHMENT_TRANSFORM = QStringLiteral("transform");
|
||||||
|
static const QString JSON_ATTACHMENT_IS_SOFT = QStringLiteral("isSoft");
|
||||||
|
|
||||||
QJsonObject AttachmentData::toJson() const {
|
QJsonObject AttachmentData::toJson() const {
|
||||||
QJsonObject result;
|
QJsonObject result;
|
||||||
|
@ -1278,6 +1279,7 @@ QJsonObject AttachmentData::toJson() const {
|
||||||
if (!transform.isIdentity()) {
|
if (!transform.isIdentity()) {
|
||||||
result[JSON_ATTACHMENT_TRANSFORM] = Transform::toJson(transform);
|
result[JSON_ATTACHMENT_TRANSFORM] = Transform::toJson(transform);
|
||||||
}
|
}
|
||||||
|
result[JSON_ATTACHMENT_IS_SOFT] = isSoft;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1302,21 +1304,25 @@ void AttachmentData::fromJson(const QJsonObject& json) {
|
||||||
rotation = transform.getRotation();
|
rotation = transform.getRotation();
|
||||||
scale = transform.getScale().x;
|
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 {
|
bool AttachmentData::operator==(const AttachmentData& other) const {
|
||||||
return modelURL == other.modelURL && jointName == other.jointName && translation == other.translation &&
|
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) {
|
QDataStream& operator<<(QDataStream& out, const AttachmentData& attachment) {
|
||||||
return out << attachment.modelURL << attachment.jointName <<
|
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) {
|
QDataStream& operator>>(QDataStream& in, AttachmentData& attachment) {
|
||||||
return in >> attachment.modelURL >> attachment.jointName >>
|
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 {
|
void AttachmentDataObject::setModelURL(const QString& modelURL) const {
|
||||||
|
|
|
@ -437,9 +437,10 @@ public:
|
||||||
glm::vec3 translation;
|
glm::vec3 translation;
|
||||||
glm::quat rotation;
|
glm::quat rotation;
|
||||||
float scale { 1.0f };
|
float scale { 1.0f };
|
||||||
|
bool isSoft { false };
|
||||||
|
|
||||||
bool isValid() const { return modelURL.isValid(); }
|
bool isValid() const { return modelURL.isValid(); }
|
||||||
|
|
||||||
bool operator==(const AttachmentData& other) const;
|
bool operator==(const AttachmentData& other) const;
|
||||||
|
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
|
|
|
@ -44,7 +44,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP;
|
return VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP;
|
||||||
case PacketType::AvatarData:
|
case PacketType::AvatarData:
|
||||||
case PacketType::BulkAvatarData:
|
case PacketType::BulkAvatarData:
|
||||||
return 17;
|
return static_cast<PacketVersion>(AvatarMixerPacketVersion::SoftAttachmentSupport);
|
||||||
default:
|
default:
|
||||||
return 17;
|
return 17;
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,4 +163,9 @@ const PacketVersion VERSION_ENTITIES_POLYLINE_TEXTURE = 50;
|
||||||
const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51;
|
const PacketVersion VERSION_ENTITIES_HAVE_PARENTS = 51;
|
||||||
const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
|
const PacketVersion VERSION_ENTITIES_REMOVED_START_AUTOMATICALLY_FROM_ANIMATION_PROPERTY_GROUP = 52;
|
||||||
|
|
||||||
|
enum class AvatarMixerPacketVersion : PacketVersion {
|
||||||
|
TranslationSupport = 17,
|
||||||
|
SoftAttachmentSupport
|
||||||
|
};
|
||||||
|
|
||||||
#endif // hifi_PacketHeaders_h
|
#endif // hifi_PacketHeaders_h
|
||||||
|
|
|
@ -971,11 +971,14 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||||
_needsUpdateClusterMatrices = true;
|
_needsUpdateClusterMatrices = true;
|
||||||
_rig->updateAnimations(deltaTime, parentTransform);
|
_rig->updateAnimations(deltaTime, parentTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::simulateInternal(float deltaTime) {
|
void Model::simulateInternal(float deltaTime) {
|
||||||
// update the world space transforms for all joints
|
// update the world space transforms for all joints
|
||||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
|
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset);
|
||||||
updateRig(deltaTime, parentTransform);
|
updateRig(deltaTime, parentTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual
|
||||||
void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
|
void Model::updateClusterMatrices(glm::vec3 modelPosition, glm::quat modelOrientation) {
|
||||||
PerformanceTimer perfTimer("Model::updateClusterMatrices");
|
PerformanceTimer perfTimer("Model::updateClusterMatrices");
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ public:
|
||||||
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
bool getSnapModelToRegistrationPoint() { return _snapModelToRegistrationPoint; }
|
||||||
|
|
||||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
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.
|
/// Returns a reference to the shared geometry.
|
||||||
const QSharedPointer<NetworkGeometry>& getGeometry() const { return _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.
|
// hook for derived classes to be notified when setUrl invalidates the current model.
|
||||||
virtual void onInvalidate() {};
|
virtual void onInvalidate() {};
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
|
||||||
void deleteGeometry();
|
void deleteGeometry();
|
||||||
void initJointTransforms();
|
void initJointTransforms();
|
||||||
|
@ -370,7 +370,6 @@ private:
|
||||||
bool _showCollisionHull = false;
|
bool _showCollisionHull = false;
|
||||||
|
|
||||||
friend class ModelMeshPartPayload;
|
friend class ModelMeshPartPayload;
|
||||||
protected:
|
|
||||||
RigPointer _rig;
|
RigPointer _rig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue