mirror of
https://github.com/lubosz/overte.git
synced 2025-08-08 02:48:12 +02:00
commit
69a33428c0
16 changed files with 249 additions and 21 deletions
|
@ -537,6 +537,7 @@ void MyAvatar::simulate(float deltaTime) {
|
||||||
// we've achived our final adjusted position and rotation for the avatar
|
// we've achived our final adjusted position and rotation for the avatar
|
||||||
// and all of its joints, now update our attachements.
|
// and all of its joints, now update our attachements.
|
||||||
Avatar::simulateAttachments(deltaTime);
|
Avatar::simulateAttachments(deltaTime);
|
||||||
|
relayJointDataToChildren();
|
||||||
|
|
||||||
if (!_skeletonModel->hasSkeleton()) {
|
if (!_skeletonModel->hasSkeleton()) {
|
||||||
// All the simulation that can be done has been done
|
// All the simulation that can be done has been done
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
#include <shared/Camera.h>
|
#include <shared/Camera.h>
|
||||||
#include <SoftAttachmentModel.h>
|
#include <SoftAttachmentModel.h>
|
||||||
#include <render/TransitionStage.h>
|
#include <render/TransitionStage.h>
|
||||||
|
#include "ModelEntityItem.h"
|
||||||
|
#include "RenderableModelEntityItem.h"
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
|
@ -347,6 +349,65 @@ void Avatar::updateAvatarEntities() {
|
||||||
setAvatarEntityDataChanged(false);
|
setAvatarEntityDataChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Avatar::relayJointDataToChildren() {
|
||||||
|
forEachChild([&](SpatiallyNestablePointer child) {
|
||||||
|
if (child->getNestableType() == NestableType::Entity) {
|
||||||
|
auto modelEntity = std::dynamic_pointer_cast<RenderableModelEntityItem>(child);
|
||||||
|
if (modelEntity) {
|
||||||
|
if (modelEntity->getRelayParentJoints()) {
|
||||||
|
if (!modelEntity->getJointMapCompleted() || _reconstructSoftEntitiesJointMap) {
|
||||||
|
QStringList modelJointNames = modelEntity->getJointNames();
|
||||||
|
int numJoints = modelJointNames.count();
|
||||||
|
std::vector<int> map;
|
||||||
|
map.reserve(numJoints);
|
||||||
|
for (int jointIndex = 0; jointIndex < numJoints; jointIndex++) {
|
||||||
|
QString jointName = modelJointNames.at(jointIndex);
|
||||||
|
int avatarJointIndex = getJointIndex(jointName);
|
||||||
|
glm::quat jointRotation;
|
||||||
|
glm::vec3 jointTranslation;
|
||||||
|
if (avatarJointIndex < 0) {
|
||||||
|
jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex);
|
||||||
|
jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex);
|
||||||
|
map.push_back(-1);
|
||||||
|
} else {
|
||||||
|
int jointIndex = getJointIndex(jointName);
|
||||||
|
jointRotation = getJointRotation(jointIndex);
|
||||||
|
jointTranslation = getJointTranslation(jointIndex);
|
||||||
|
map.push_back(avatarJointIndex);
|
||||||
|
}
|
||||||
|
modelEntity->setLocalJointRotation(jointIndex, jointRotation);
|
||||||
|
modelEntity->setLocalJointTranslation(jointIndex, jointTranslation);
|
||||||
|
}
|
||||||
|
modelEntity->setJointMap(map);
|
||||||
|
} else {
|
||||||
|
QStringList modelJointNames = modelEntity->getJointNames();
|
||||||
|
int numJoints = modelJointNames.count();
|
||||||
|
for (int jointIndex = 0; jointIndex < numJoints; jointIndex++) {
|
||||||
|
int avatarJointIndex = modelEntity->avatarJointIndex(jointIndex);
|
||||||
|
glm::quat jointRotation;
|
||||||
|
glm::vec3 jointTranslation;
|
||||||
|
if (avatarJointIndex >=0) {
|
||||||
|
jointRotation = getJointRotation(avatarJointIndex);
|
||||||
|
jointTranslation = getJointTranslation(avatarJointIndex);
|
||||||
|
} else {
|
||||||
|
jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex);
|
||||||
|
jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex);
|
||||||
|
}
|
||||||
|
modelEntity->setLocalJointRotation(jointIndex, jointRotation);
|
||||||
|
modelEntity->setLocalJointTranslation(jointIndex, jointTranslation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Transform avatarTransform = _skeletonModel->getTransform();
|
||||||
|
avatarTransform.setScale(_skeletonModel->getScale());
|
||||||
|
modelEntity->setOverrideTransform(avatarTransform, _skeletonModel->getOffset());
|
||||||
|
modelEntity->simulateRelayedJoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_reconstructSoftEntitiesJointMap = false;
|
||||||
|
}
|
||||||
|
|
||||||
void Avatar::simulate(float deltaTime, bool inView) {
|
void Avatar::simulate(float deltaTime, bool inView) {
|
||||||
PROFILE_RANGE(simulation, "simulate");
|
PROFILE_RANGE(simulation, "simulate");
|
||||||
|
|
||||||
|
@ -379,6 +440,7 @@ void Avatar::simulate(float deltaTime, bool inView) {
|
||||||
}
|
}
|
||||||
head->setScale(getModelScale());
|
head->setScale(getModelScale());
|
||||||
head->simulate(deltaTime);
|
head->simulate(deltaTime);
|
||||||
|
relayJointDataToChildren();
|
||||||
} else {
|
} else {
|
||||||
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
// a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated.
|
||||||
_skeletonModel->simulate(deltaTime, false);
|
_skeletonModel->simulate(deltaTime, false);
|
||||||
|
@ -1197,6 +1259,7 @@ void Avatar::setModelURLFinished(bool success) {
|
||||||
invalidateJointIndicesCache();
|
invalidateJointIndicesCache();
|
||||||
|
|
||||||
_isAnimatingScale = true;
|
_isAnimatingScale = true;
|
||||||
|
_reconstructSoftEntitiesJointMap = true;
|
||||||
|
|
||||||
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
|
if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) {
|
||||||
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
|
const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts
|
||||||
|
|
|
@ -330,6 +330,7 @@ protected:
|
||||||
|
|
||||||
// protected methods...
|
// protected methods...
|
||||||
bool isLookingAtMe(AvatarSharedPointer avatar) const;
|
bool isLookingAtMe(AvatarSharedPointer avatar) const;
|
||||||
|
void relayJointDataToChildren();
|
||||||
|
|
||||||
void fade(render::Transaction& transaction, render::Transition::Type type);
|
void fade(render::Transaction& transaction, render::Transition::Type type);
|
||||||
|
|
||||||
|
@ -382,6 +383,7 @@ protected:
|
||||||
bool _isAnimatingScale { false };
|
bool _isAnimatingScale { false };
|
||||||
bool _mustFadeIn { false };
|
bool _mustFadeIn { false };
|
||||||
bool _isFading { false };
|
bool _isFading { false };
|
||||||
|
bool _reconstructSoftEntitiesJointMap { false };
|
||||||
float _modelScale { 1.0f };
|
float _modelScale { 1.0f };
|
||||||
|
|
||||||
static int _jointConesID;
|
static int _jointConesID;
|
||||||
|
|
|
@ -708,6 +708,26 @@ void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderableModelEntityItem::setJointMap(std::vector<int> jointMap) {
|
||||||
|
if (jointMap.size() > 0) {
|
||||||
|
_jointMap = jointMap;
|
||||||
|
_jointMapCompleted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_jointMapCompleted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) {
|
||||||
|
int result = -1;
|
||||||
|
int mapSize = (int) _jointMap.size();
|
||||||
|
if (modelJointIndex >=0 && modelJointIndex < mapSize) {
|
||||||
|
result = _jointMap[modelJointIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool RenderableModelEntityItem::contains(const glm::vec3& point) const {
|
bool RenderableModelEntityItem::contains(const glm::vec3& point) const {
|
||||||
auto model = getModel();
|
auto model = getModel();
|
||||||
if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) {
|
if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) {
|
||||||
|
@ -813,6 +833,10 @@ bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int ind
|
||||||
return setLocalJointTranslation(index, jointRelativePose.trans());
|
return setLocalJointTranslation(index, jointRelativePose.trans());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RenderableModelEntityItem::getJointMapCompleted() {
|
||||||
|
return _jointMapCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
glm::quat RenderableModelEntityItem::getLocalJointRotation(int index) const {
|
glm::quat RenderableModelEntityItem::getLocalJointRotation(int index) const {
|
||||||
auto model = getModel();
|
auto model = getModel();
|
||||||
if (model) {
|
if (model) {
|
||||||
|
@ -835,6 +859,13 @@ glm::vec3 RenderableModelEntityItem::getLocalJointTranslation(int index) const {
|
||||||
return glm::vec3();
|
return glm::vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderableModelEntityItem::setOverrideTransform(const Transform& transform, const glm::vec3& offset) {
|
||||||
|
auto model = getModel();
|
||||||
|
if (model) {
|
||||||
|
model->overrideModelTransformAndOffset(transform, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool RenderableModelEntityItem::setLocalJointRotation(int index, const glm::quat& rotation) {
|
bool RenderableModelEntityItem::setLocalJointRotation(int index, const glm::quat& rotation) {
|
||||||
autoResizeJointArrays();
|
autoResizeJointArrays();
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
@ -929,6 +960,26 @@ bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) {
|
||||||
return !result.isEmpty();
|
return !result.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderableModelEntityItem::simulateRelayedJoints() {
|
||||||
|
ModelPointer model = getModel();
|
||||||
|
if (model && model->isLoaded()) {
|
||||||
|
copyAnimationJointDataToModel();
|
||||||
|
model->simulate(0.0f);
|
||||||
|
model->updateRenderItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenderableModelEntityItem::stopModelOverrideIfNoParent() {
|
||||||
|
auto model = getModel();
|
||||||
|
if (model) {
|
||||||
|
bool overriding = model->isOverridingModelTransformAndOffset();
|
||||||
|
QUuid parentID = getParentID();
|
||||||
|
if (overriding && (!_relayParentJoints || parentID.isNull())) {
|
||||||
|
model->stopTransformAndOffsetOverride();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
void RenderableModelEntityItem::copyAnimationJointDataToModel() {
|
||||||
auto model = getModel();
|
auto model = getModel();
|
||||||
if (!model || !model->isLoaded()) {
|
if (!model || !model->isLoaded()) {
|
||||||
|
@ -1280,6 +1331,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
||||||
}
|
}
|
||||||
|
|
||||||
entity->updateModelBounds();
|
entity->updateModelBounds();
|
||||||
|
entity->stopModelOverrideIfNoParent();
|
||||||
|
|
||||||
if (model->isVisible() != _visible) {
|
if (model->isVisible() != _visible) {
|
||||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||||
|
|
|
@ -81,8 +81,14 @@ public:
|
||||||
void setCollisionShape(const btCollisionShape* shape) override;
|
void setCollisionShape(const btCollisionShape* shape) override;
|
||||||
|
|
||||||
virtual bool contains(const glm::vec3& point) const override;
|
virtual bool contains(const glm::vec3& point) const override;
|
||||||
|
void stopModelOverrideIfNoParent();
|
||||||
|
|
||||||
virtual bool shouldBePhysical() const override;
|
virtual bool shouldBePhysical() const override;
|
||||||
|
void simulateRelayedJoints();
|
||||||
|
bool getJointMapCompleted();
|
||||||
|
void setJointMap(std::vector<int> jointMap);
|
||||||
|
int avatarJointIndex(int modelJointIndex);
|
||||||
|
void setOverrideTransform(const Transform& transform, const glm::vec3& offset);
|
||||||
|
|
||||||
// these are in the frame of this object (model space)
|
// these are in the frame of this object (model space)
|
||||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||||
|
@ -90,7 +96,6 @@ public:
|
||||||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override;
|
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override;
|
||||||
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override;
|
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override;
|
||||||
|
|
||||||
|
|
||||||
virtual glm::quat getLocalJointRotation(int index) const override;
|
virtual glm::quat getLocalJointRotation(int index) const override;
|
||||||
virtual glm::vec3 getLocalJointTranslation(int index) const override;
|
virtual glm::vec3 getLocalJointTranslation(int index) const override;
|
||||||
virtual bool setLocalJointRotation(int index, const glm::quat& rotation) override;
|
virtual bool setLocalJointRotation(int index, const glm::quat& rotation) override;
|
||||||
|
@ -119,7 +124,9 @@ private:
|
||||||
|
|
||||||
void getCollisionGeometryResource();
|
void getCollisionGeometryResource();
|
||||||
GeometryResource::Pointer _compoundShapeResource;
|
GeometryResource::Pointer _compoundShapeResource;
|
||||||
|
bool _jointMapCompleted { false };
|
||||||
bool _originalTexturesRead { false };
|
bool _originalTexturesRead { false };
|
||||||
|
std::vector<int> _jointMap;
|
||||||
QVariantMap _originalTextures;
|
QVariantMap _originalTextures;
|
||||||
bool _dimensionsInitialized { true };
|
bool _dimensionsInitialized { true };
|
||||||
bool _needsJointSimulation { false };
|
bool _needsJointSimulation { false };
|
||||||
|
|
|
@ -395,6 +395,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||||
|
|
||||||
CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape);
|
CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape);
|
||||||
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
CHECK_PROPERTY_CHANGE(PROP_DPI, dpi);
|
||||||
|
CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
||||||
|
|
||||||
changedProperties += _animation.getChangedProperties();
|
changedProperties += _animation.getChangedProperties();
|
||||||
changedProperties += _keyLight.getChangedProperties();
|
changedProperties += _keyLight.getChangedProperties();
|
||||||
|
@ -527,6 +528,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS, jointRotations);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS, jointRotations);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet);
|
||||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations);
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations);
|
||||||
|
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) {
|
if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) {
|
||||||
|
@ -755,6 +757,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart);
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish);
|
||||||
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints);
|
||||||
|
|
||||||
// Certifiable Properties
|
// Certifiable Properties
|
||||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
|
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
|
||||||
|
@ -1163,6 +1166,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
||||||
ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector<glm::quat>);
|
ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector<glm::quat>);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>);
|
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector<bool>);
|
||||||
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>);
|
ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector<glm::vec3>);
|
||||||
|
ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool);
|
||||||
|
|
||||||
ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString);
|
ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString);
|
||||||
|
|
||||||
|
@ -1386,6 +1390,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
||||||
APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, properties.getJointRotations());
|
APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, properties.getJointRotations());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, properties.getJointTranslationsSet());
|
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, properties.getJointTranslationsSet());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations());
|
APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.getType() == EntityTypes::Light) {
|
if (properties.getType() == EntityTypes::Light) {
|
||||||
|
@ -1745,6 +1750,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector<glm::quat>, setJointRotations);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector<glm::quat>, setJointRotations);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector<bool>, setJointTranslationsSet);
|
||||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector<glm::vec3>, setJointTranslations);
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector<glm::vec3>, setJointTranslations);
|
||||||
|
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties.getType() == EntityTypes::Light) {
|
if (properties.getType() == EntityTypes::Light) {
|
||||||
|
@ -2089,6 +2095,7 @@ void EntityItemProperties::markAllChanged() {
|
||||||
_owningAvatarIDChanged = true;
|
_owningAvatarIDChanged = true;
|
||||||
|
|
||||||
_dpiChanged = true;
|
_dpiChanged = true;
|
||||||
|
_relayParentJointsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The minimum bounding box for the entity.
|
// The minimum bounding box for the entity.
|
||||||
|
@ -2455,6 +2462,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
||||||
if (jointTranslationsChanged()) {
|
if (jointTranslationsChanged()) {
|
||||||
out += "jointTranslations";
|
out += "jointTranslations";
|
||||||
}
|
}
|
||||||
|
if (relayParentJointsChanged()) {
|
||||||
|
out += "relayParentJoints";
|
||||||
|
}
|
||||||
if (queryAACubeChanged()) {
|
if (queryAACubeChanged()) {
|
||||||
out += "queryAACube";
|
out += "queryAACube";
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,6 +255,7 @@ public:
|
||||||
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
|
DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY);
|
||||||
|
|
||||||
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
|
DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS);
|
||||||
|
DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS);
|
||||||
|
|
||||||
static QString getComponentModeString(uint32_t mode);
|
static QString getComponentModeString(uint32_t mode);
|
||||||
static QString getComponentModeAsString(uint32_t mode);
|
static QString getComponentModeAsString(uint32_t mode);
|
||||||
|
|
|
@ -92,4 +92,6 @@ const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30;
|
||||||
|
|
||||||
const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid();
|
const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid();
|
||||||
|
|
||||||
|
const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false;
|
||||||
|
|
||||||
#endif // hifi_EntityItemPropertiesDefaults_h
|
#endif // hifi_EntityItemPropertiesDefaults_h
|
||||||
|
|
|
@ -40,6 +40,7 @@ enum EntityPropertyList {
|
||||||
PROP_ANIMATION_FRAME_INDEX,
|
PROP_ANIMATION_FRAME_INDEX,
|
||||||
PROP_ANIMATION_PLAYING,
|
PROP_ANIMATION_PLAYING,
|
||||||
PROP_ANIMATION_ALLOW_TRANSLATION,
|
PROP_ANIMATION_ALLOW_TRANSLATION,
|
||||||
|
PROP_RELAY_PARENT_JOINTS,
|
||||||
|
|
||||||
// these properties are supported by the EntityItem base class
|
// these properties are supported by the EntityItem base class
|
||||||
PROP_REGISTRATION_POINT,
|
PROP_REGISTRATION_POINT,
|
||||||
|
|
|
@ -62,7 +62,7 @@ EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredP
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointRotations, getJointRotations);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointRotations, getJointRotations);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslationsSet, getJointTranslationsSet);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslationsSet, getJointTranslationsSet);
|
||||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations);
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations);
|
||||||
|
COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints);
|
||||||
_animationProperties.getProperties(properties);
|
_animationProperties.getProperties(properties);
|
||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotations, setJointRotations);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotations, setJointRotations);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslationsSet, setJointTranslationsSet);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslationsSet, setJointTranslationsSet);
|
||||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations);
|
||||||
|
SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints);
|
||||||
|
|
||||||
bool somethingChangedInAnimations = _animationProperties.setProperties(properties);
|
bool somethingChangedInAnimations = _animationProperties.setProperties(properties);
|
||||||
|
|
||||||
|
@ -115,6 +116,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
||||||
READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL);
|
READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL);
|
||||||
READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
|
READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL);
|
||||||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||||
|
READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints);
|
||||||
|
|
||||||
int bytesFromAnimation;
|
int bytesFromAnimation;
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
|
@ -155,6 +157,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
|
||||||
requestedProperties += PROP_JOINT_ROTATIONS;
|
requestedProperties += PROP_JOINT_ROTATIONS;
|
||||||
requestedProperties += PROP_JOINT_TRANSLATIONS_SET;
|
requestedProperties += PROP_JOINT_TRANSLATIONS_SET;
|
||||||
requestedProperties += PROP_JOINT_TRANSLATIONS;
|
requestedProperties += PROP_JOINT_TRANSLATIONS;
|
||||||
|
requestedProperties += PROP_RELAY_PARENT_JOINTS;
|
||||||
|
|
||||||
return requestedProperties;
|
return requestedProperties;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +176,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
||||||
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, getModelURL());
|
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, getModelURL());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL());
|
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL());
|
||||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures());
|
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures());
|
||||||
|
APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints());
|
||||||
|
|
||||||
withReadLock([&] {
|
withReadLock([&] {
|
||||||
_animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
_animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||||
|
@ -586,6 +590,18 @@ QString ModelEntityItem::getModelURL() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelEntityItem::setRelayParentJoints(bool relayJoints) {
|
||||||
|
withWriteLock([&] {
|
||||||
|
_relayParentJoints = relayJoints;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModelEntityItem::getRelayParentJoints() const {
|
||||||
|
return resultWithReadLock<bool>([&] {
|
||||||
|
return _relayParentJoints;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
QString ModelEntityItem::getCompoundShapeURL() const {
|
QString ModelEntityItem::getCompoundShapeURL() const {
|
||||||
return _compoundShapeURL.get();
|
return _compoundShapeURL.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,9 @@ public:
|
||||||
void setAnimationHold(bool hold);
|
void setAnimationHold(bool hold);
|
||||||
bool getAnimationHold() const;
|
bool getAnimationHold() const;
|
||||||
|
|
||||||
|
void setRelayParentJoints(bool relayJoints);
|
||||||
|
bool getRelayParentJoints() const;
|
||||||
|
|
||||||
void setAnimationFirstFrame(float firstFrame);
|
void setAnimationFirstFrame(float firstFrame);
|
||||||
float getAnimationFirstFrame() const;
|
float getAnimationFirstFrame() const;
|
||||||
|
|
||||||
|
@ -157,6 +160,7 @@ protected:
|
||||||
|
|
||||||
rgbColor _color;
|
rgbColor _color;
|
||||||
QString _modelURL;
|
QString _modelURL;
|
||||||
|
bool _relayParentJoints;
|
||||||
|
|
||||||
ThreadSafeValueCache<QString> _compoundShapeURL;
|
ThreadSafeValueCache<QString> _compoundShapeURL;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return static_cast<PacketVersion>(EntityVersion::ZoneStageRemoved);
|
return static_cast<PacketVersion>(EntityVersion::SoftEntities);
|
||||||
|
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::RemovedJurisdictions);
|
||||||
|
|
|
@ -205,7 +205,8 @@ enum class EntityVersion : PacketVersion {
|
||||||
StaticCertJsonVersionOne,
|
StaticCertJsonVersionOne,
|
||||||
OwnershipChallengeFix,
|
OwnershipChallengeFix,
|
||||||
ZoneLightInheritModes = 82,
|
ZoneLightInheritModes = 82,
|
||||||
ZoneStageRemoved
|
ZoneStageRemoved,
|
||||||
|
SoftEntities
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||||
|
|
|
@ -145,7 +145,13 @@ void Model::setTransformNoUpdateRenderItems(const Transform& transform) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform Model::getTransform() const {
|
Transform Model::getTransform() const {
|
||||||
if (_spatiallyNestableOverride) {
|
if (_overrideModelTransform) {
|
||||||
|
Transform transform;
|
||||||
|
transform.setTranslation(getOverrideTranslation());
|
||||||
|
transform.setRotation(getOverrideRotation());
|
||||||
|
transform.setScale(getScale());
|
||||||
|
return transform;
|
||||||
|
} else if (_spatiallyNestableOverride) {
|
||||||
bool success;
|
bool success;
|
||||||
Transform transform = _spatiallyNestableOverride->getTransform(success);
|
Transform transform = _spatiallyNestableOverride->getTransform(success);
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -1381,6 +1387,14 @@ void Model::deleteGeometry() {
|
||||||
_collisionGeometry.reset();
|
_collisionGeometry.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::overrideModelTransformAndOffset(const Transform& transform, const glm::vec3& offset) {
|
||||||
|
_overrideTranslation = transform.getTranslation();
|
||||||
|
_overrideRotation = transform.getRotation();
|
||||||
|
_overrideModelTransform = true;
|
||||||
|
setScale(transform.getScale());
|
||||||
|
setOffset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
AABox Model::getRenderableMeshBound() const {
|
AABox Model::getRenderableMeshBound() const {
|
||||||
if (!isLoaded()) {
|
if (!isLoaded()) {
|
||||||
return AABox();
|
return AABox();
|
||||||
|
|
|
@ -212,10 +212,15 @@ public:
|
||||||
|
|
||||||
void setTranslation(const glm::vec3& translation);
|
void setTranslation(const glm::vec3& translation);
|
||||||
void setRotation(const glm::quat& rotation);
|
void setRotation(const glm::quat& rotation);
|
||||||
|
void overrideModelTransformAndOffset(const Transform& transform, const glm::vec3& offset);
|
||||||
|
bool isOverridingModelTransformAndOffset() { return _overrideModelTransform; };
|
||||||
|
void stopTransformAndOffsetOverride() { _overrideModelTransform = false; };
|
||||||
void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK
|
void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK
|
||||||
|
|
||||||
const glm::vec3& getTranslation() const { return _translation; }
|
const glm::vec3& getTranslation() const { return _translation; }
|
||||||
const glm::quat& getRotation() const { return _rotation; }
|
const glm::quat& getRotation() const { return _rotation; }
|
||||||
|
const glm::vec3& getOverrideTranslation() const { return _overrideTranslation; }
|
||||||
|
const glm::quat& getOverrideRotation() const { return _overrideRotation; }
|
||||||
|
|
||||||
glm::vec3 getNaturalDimensions() const;
|
glm::vec3 getNaturalDimensions() const;
|
||||||
|
|
||||||
|
@ -343,6 +348,9 @@ protected:
|
||||||
glm::quat _rotation;
|
glm::quat _rotation;
|
||||||
glm::vec3 _scale;
|
glm::vec3 _scale;
|
||||||
|
|
||||||
|
glm::vec3 _overrideTranslation;
|
||||||
|
glm::quat _overrideRotation;
|
||||||
|
|
||||||
// For entity models this is the translation for the minimum extent of the model (in original mesh coordinate space)
|
// For entity models this is the translation for the minimum extent of the model (in original mesh coordinate space)
|
||||||
// to the model's registration point. For avatar models this is the translation from the avatar's hips, as determined
|
// to the model's registration point. For avatar models this is the translation from the avatar's hips, as determined
|
||||||
// by the default pose, to the origin.
|
// by the default pose, to the origin.
|
||||||
|
@ -403,6 +411,7 @@ protected:
|
||||||
|
|
||||||
QMutex _mutex;
|
QMutex _mutex;
|
||||||
|
|
||||||
|
bool _overrideModelTransform { false };
|
||||||
bool _triangleSetsValid { false };
|
bool _triangleSetsValid { false };
|
||||||
void calculateTriangleSets();
|
void calculateTriangleSets();
|
||||||
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
|
QVector<TriangleSet> _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes
|
||||||
|
|
|
@ -57,7 +57,7 @@ ToggleButtonBuddy.prototype.addToggleHandler = function (callback) {
|
||||||
};
|
};
|
||||||
ToggleButtonBuddy.prototype.removeToggleHandler = function (callback) {
|
ToggleButtonBuddy.prototype.removeToggleHandler = function (callback) {
|
||||||
var index = this.callbacks.indexOf(callback);
|
var index = this.callbacks.indexOf(callback);
|
||||||
if (index != -1) {
|
if (index !== -1) {
|
||||||
this.callbacks.splice(index, 1);
|
this.callbacks.splice(index, 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -86,13 +86,23 @@ var coatButton = new ToggleButtonBuddy(buttonPositionX, buttonPositionY, BUTTON_
|
||||||
down: "https://s3.amazonaws.com/hifi-public/tony/icons/coat-down.svg"
|
down: "https://s3.amazonaws.com/hifi-public/tony/icons/coat-down.svg"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
buttonPositionY += BUTTON_HEIGHT + BUTTON_PADDING;
|
||||||
|
var coatButton2 = new ToggleButtonBuddy(buttonPositionX, buttonPositionY, BUTTON_WIDTH, BUTTON_HEIGHT, {
|
||||||
|
up: "https://s3.amazonaws.com/hifi-public/tony/icons/coat-up.svg",
|
||||||
|
down: "https://s3.amazonaws.com/hifi-public/tony/icons/coat-down.svg"
|
||||||
|
});
|
||||||
|
|
||||||
|
var AVATAR_ATTACHMENT = 0;
|
||||||
|
var AVATAR_SOFT_ATTACHMENT = 1;
|
||||||
|
var ENTITY_ATTACHMENT = 2;
|
||||||
|
|
||||||
var HAT_ATTACHMENT = {
|
var HAT_ATTACHMENT = {
|
||||||
modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
|
modelURL: "https://s3.amazonaws.com/hifi-public/tony/cowboy-hat.fbx",
|
||||||
jointName: "Head",
|
jointName: "Head",
|
||||||
translation: {"x": 0, "y": 0.25, "z": 0.03},
|
translation: {"x": 0, "y": 0.25, "z": 0.03},
|
||||||
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
||||||
scale: 0.052,
|
scale: 0.052,
|
||||||
isSoft: false
|
type: AVATAR_ATTACHMENT
|
||||||
};
|
};
|
||||||
|
|
||||||
var COAT_ATTACHMENT = {
|
var COAT_ATTACHMENT = {
|
||||||
|
@ -101,7 +111,16 @@ var COAT_ATTACHMENT = {
|
||||||
translation: {"x": 0, "y": 0, "z": 0},
|
translation: {"x": 0, "y": 0, "z": 0},
|
||||||
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
||||||
scale: 1,
|
scale: 1,
|
||||||
isSoft: true
|
type: AVATAR_SOFT_ATTACHMENT
|
||||||
|
};
|
||||||
|
|
||||||
|
var COAT_ENTITY_ATTACHMENT = {
|
||||||
|
modelURL: "https://hifi-content.s3.amazonaws.com/ozan/dev/clothes/coat/coat_rig.fbx",
|
||||||
|
jointName: "Hips",
|
||||||
|
translation: {"x": 0, "y": 0, "z": 0},
|
||||||
|
rotation: {"x": 0, "y": 0, "z": 0, "w": 1},
|
||||||
|
scale: 1,
|
||||||
|
type: ENTITY_ATTACHMENT
|
||||||
};
|
};
|
||||||
|
|
||||||
hatButton.addToggleHandler(function (isDown) {
|
hatButton.addToggleHandler(function (isDown) {
|
||||||
|
@ -120,28 +139,54 @@ coatButton.addToggleHandler(function (isDown) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
coatButton2.addToggleHandler(function (isDown) {
|
||||||
|
if (isDown) {
|
||||||
|
wearAttachment(COAT_ENTITY_ATTACHMENT);
|
||||||
|
} else {
|
||||||
|
removeAttachment(COAT_ENTITY_ATTACHMENT);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function wearAttachment(attachment) {
|
function wearAttachment(attachment) {
|
||||||
MyAvatar.attach(attachment.modelURL,
|
if (attachment.type === AVATAR_ATTACHMENT || attachment.type === AVATAR_SOFT_ATTACHMENT) {
|
||||||
attachment.jointName,
|
MyAvatar.attach(attachment.modelURL,
|
||||||
attachment.translation,
|
attachment.jointName,
|
||||||
attachment.rotation,
|
attachment.translation,
|
||||||
attachment.scale,
|
attachment.rotation,
|
||||||
attachment.isSoft);
|
attachment.scale,
|
||||||
|
(attachment.type === AVATAR_SOFT_ATTACHMENT));
|
||||||
|
} else {
|
||||||
|
attachment.entityID = Entities.addEntity({
|
||||||
|
name: "attachment",
|
||||||
|
type: "Model",
|
||||||
|
modelURL: attachment.modelURL,
|
||||||
|
parentID: MyAvatar.sessionUUID,
|
||||||
|
relayParentJoints: true,
|
||||||
|
position: attachment.position,
|
||||||
|
rotation: attachment.rotation,
|
||||||
|
parentJointIndex: -1
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeAttachment(attachment) {
|
function removeAttachment(attachment) {
|
||||||
var attachments = MyAvatar.attachmentData;
|
if (attachment.type === AVATAR_ATTACHMENT || attachment.type === AVATAR_SOFT_ATTACHMENT) {
|
||||||
var i, l = attachments.length;
|
var attachments = MyAvatar.attachmentData;
|
||||||
for (i = 0; i < l; i++) {
|
var i, l = attachments.length;
|
||||||
if (attachments[i].modelURL === attachment.modelURL) {
|
for (i = 0; i < l; i++) {
|
||||||
attachments.splice(i, 1);
|
if (attachments[i].modelURL === attachment.modelURL) {
|
||||||
MyAvatar.attachmentData = attachments;
|
attachments.splice(i, 1);
|
||||||
break;
|
MyAvatar.attachmentData = attachments;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Entities.deleteEntity(attachment.entityID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.scriptEnding.connect(function() {
|
Script.scriptEnding.connect(function() {
|
||||||
hatButton.destroy();
|
hatButton.destroy();
|
||||||
coatButton.destroy();
|
coatButton.destroy();
|
||||||
|
coatButton2.destroy();
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue