diff --git a/domain-server/resources/web/wizard/js/wizard.js b/domain-server/resources/web/wizard/js/wizard.js
index 57e85973f4..c0d17ca02c 100644
--- a/domain-server/resources/web/wizard/js/wizard.js
+++ b/domain-server/resources/web/wizard/js/wizard.js
@@ -11,7 +11,7 @@ $(document).ready(function(){
$('#connect-account-btn').attr('href', URLs.METAVERSE_URL + "/user/tokens/new?for_domain_server=true");
$('[data-toggle="tooltip"]').tooltip();
-
+
$('.perms-link').on('click', function() {
var modal_body = '
';
modal_body += '
None - No one will have permissions. Only you and the users your have given administrator privileges to will have permissions.';
@@ -70,8 +70,8 @@ $(document).ready(function(){
});
});
- $('body').on('click', '#explore-settings', function() {
- exploreSettings();
+ $('body').on('click', '#visit-domain', function() {
+ $('#share-link')[0].click();
});
$('input[type=radio][name=connect-radio]').change(function() {
@@ -120,6 +120,14 @@ $(document).ready(function(){
});
});
+function copyToClipboard(element) {
+ var $temp = $("
");
+ $("body").append($temp);
+ $temp.val($(element).text()).select();
+ document.execCommand("copy");
+ $temp.remove();
+}
+
function setupWizardSteps() {
currentStepNumber = Settings.data.values.wizard.steps_completed;
var steps = null;
@@ -155,7 +163,9 @@ function setupWizardSteps() {
function updatePlaceNameLink(address) {
if (address) {
- $('#place-name-link').html('Your domain is reachable at:
' + address + '');
+ var url = URLs.PLACE_URL + '/' + address;
+ $('#place-name-link').html('Your domain is reachable at:
' + address + '');
+ $('#share-field a').attr('href', url).text(url);
}
}
@@ -507,14 +517,3 @@ function saveUsernamePassword() {
location.reload();
});
}
-
-function exploreSettings() {
- if ($('#go-to-domain').is(":checked")) {
- var link = $('#place-name-link a:first');
- if (link.length > 0) {
- window.open(link.attr("href"));
- }
- }
-
- goToNextStep();
-}
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 8a290aece7..55258e9a04 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1185,6 +1185,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
_entityEditSender.setServerJurisdictions(&_entityServerJurisdictions);
_entityEditSender.setMyAvatar(myAvatar.get());
+ // The entity octree will have to know about MyAvatar for the parentJointName import
+ getEntities()->getTree()->setMyAvatar(myAvatar);
+ _entityClipboard->setMyAvatar(myAvatar);
+
// For now we're going to set the PPS for outbound packets to be super high, this is
// probably not the right long term solution. But for now, we're going to do this to
// allow you to move an entity around in your hand
@@ -3999,6 +4003,7 @@ bool Application::exportEntities(const QString& filename,
auto entityTree = getEntities()->getTree();
auto exportTree = std::make_shared
();
+ exportTree->setMyAvatar(getMyAvatar());
exportTree->createRootElement();
glm::vec3 root(TREE_SCALE, TREE_SCALE, TREE_SCALE);
bool success = true;
diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp
index 9d19b8fb0f..4cfa4d6156 100644
--- a/interface/src/SecondaryCamera.cpp
+++ b/interface/src/SecondaryCamera.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
using RenderArgsPointer = std::shared_ptr;
@@ -48,6 +49,47 @@ public:
_farClipPlaneDistance = config.farClipPlaneDistance;
_textureWidth = config.textureWidth;
_textureHeight = config.textureHeight;
+ _mirrorProjection = config.mirrorProjection;
+ }
+
+ void setMirrorProjection(ViewFrustum& srcViewFrustum) {
+ if (_attachedEntityId.isNull()) {
+ qWarning() << "ERROR: Cannot set mirror projection for SecondaryCamera without an attachedEntityId set.";
+ return;
+ }
+
+ EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_attachedEntityId,
+ _attachedEntityPropertyFlags);
+ glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition();
+ glm::quat mirrorPropertiesRotation = entityProperties.getRotation();
+ glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions();
+ glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions;
+
+ // setup mirror from world as inverse of world from mirror transformation using inverted x and z for mirrored image
+ // TODO: we are assuming here that UP is world y-axis
+ glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation) * glm::scale(vec3(-1.0f, 1.0f, -1.0f));
+ glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition);
+ glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation;
+ glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror);
+
+ // get mirror camera position by reflecting main camera position's z coordinate in mirror space
+ glm::vec3 mainCameraPositionWorld = qApp->getCamera().getPosition();
+ glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f));
+ glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y,
+ -mainCameraPositionMirror.z);
+ glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f));
+
+ // set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation
+ glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation);
+ srcViewFrustum.setPosition(mirrorCameraPositionWorld);
+ srcViewFrustum.setOrientation(mirrorCameraOrientation);
+
+ // build frustum using mirror space translation of mirrored camera
+ float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f;
+ glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+ glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+ glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, _farClipPlaneDistance);
+ srcViewFrustum.setProjection(frustum);
}
void run(const render::RenderContextPointer& renderContext, RenderArgsPointer& cachedArgs) {
@@ -71,15 +113,22 @@ public:
});
auto srcViewFrustum = args->getViewFrustum();
- if (!_attachedEntityId.isNull()) {
- EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_attachedEntityId, _attachedEntityPropertyFlags);
- srcViewFrustum.setPosition(entityProperties.getPosition());
- srcViewFrustum.setOrientation(entityProperties.getRotation());
+ if (_mirrorProjection) {
+ setMirrorProjection(srcViewFrustum);
} else {
- srcViewFrustum.setPosition(_position);
- srcViewFrustum.setOrientation(_orientation);
+ if (!_attachedEntityId.isNull()) {
+ EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_attachedEntityId,
+ _attachedEntityPropertyFlags);
+ srcViewFrustum.setPosition(entityProperties.getPosition());
+ srcViewFrustum.setOrientation(entityProperties.getRotation());
+ } else {
+ srcViewFrustum.setPosition(_position);
+ srcViewFrustum.setOrientation(_orientation);
+ }
+ srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV),
+ ((float)args->_viewport.z / (float)args->_viewport.w),
+ _nearClipPlaneDistance, _farClipPlaneDistance));
}
- srcViewFrustum.setProjection(glm::perspective(glm::radians(_vFoV), ((float)args->_viewport.z / (float)args->_viewport.w), _nearClipPlaneDistance, _farClipPlaneDistance));
// Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera,
// which is not what we want here.
srcViewFrustum.calculate();
@@ -101,6 +150,7 @@ private:
float _farClipPlaneDistance;
int _textureWidth;
int _textureHeight;
+ bool _mirrorProjection;
EntityPropertyFlags _attachedEntityPropertyFlags;
QSharedPointer _entityScriptingInterface;
};
diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h
index a9b438ec6f..38d75c391e 100644
--- a/interface/src/SecondaryCamera.h
+++ b/interface/src/SecondaryCamera.h
@@ -35,6 +35,7 @@ class SecondaryCameraJobConfig : public render::Task::Config { // Exposes second
Q_PROPERTY(float vFoV MEMBER vFoV NOTIFY dirty) // Secondary camera's vertical field of view. In degrees.
Q_PROPERTY(float nearClipPlaneDistance MEMBER nearClipPlaneDistance NOTIFY dirty) // Secondary camera's near clip plane distance. In meters.
Q_PROPERTY(float farClipPlaneDistance MEMBER farClipPlaneDistance NOTIFY dirty) // Secondary camera's far clip plane distance. In meters.
+ Q_PROPERTY(bool mirrorProjection MEMBER mirrorProjection NOTIFY dirty) // Flag to use attached mirror entity to build frustum for the mirror and set mirrored camera position/orientation.
public:
QUuid attachedEntityId;
glm::vec3 position;
@@ -44,6 +45,7 @@ public:
float farClipPlaneDistance { DEFAULT_FAR_CLIP };
int textureWidth { TextureCache::DEFAULT_SPECTATOR_CAM_WIDTH };
int textureHeight { TextureCache::DEFAULT_SPECTATOR_CAM_HEIGHT };
+ bool mirrorProjection { false };
SecondaryCameraJobConfig() : render::Task::Config(false) {}
signals:
diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp
index 0871265f71..22a4d3a2dc 100644
--- a/libraries/entities/src/EntityTree.cpp
+++ b/libraries/entities/src/EntityTree.cpp
@@ -2254,7 +2254,8 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer
entityDescription["Entities"] = QVariantList();
}
QScriptEngine scriptEngine;
- RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues, skipThoseWithBadParents);
+ RecurseOctreeToMapOperator theOperator(entityDescription, element, &scriptEngine, skipDefaultValues,
+ skipThoseWithBadParents, _myAvatar);
recurseTreeWithOperator(&theOperator);
return true;
}
@@ -2276,6 +2277,17 @@ bool EntityTree::readFromMap(QVariantMap& map) {
foreach (QVariant entityVariant, entitiesQList) {
// QVariantMap --> QScriptValue --> EntityItemProperties --> Entity
QVariantMap entityMap = entityVariant.toMap();
+
+ // handle parentJointName for wearables
+ if (_myAvatar && entityMap.contains("parentJointName") && entityMap.contains("parentID") &&
+ QUuid(entityMap["parentID"].toString()) == AVATAR_SELF_ID) {
+
+ entityMap["parentJointIndex"] = _myAvatar->getJointIndex(entityMap["parentJointName"].toString());
+
+ qCDebug(entities) << "Found parentJointName " << entityMap["parentJointName"].toString() <<
+ " mapped it to parentJointIndex " << entityMap["parentJointIndex"].toInt();
+ }
+
QScriptValue entityScriptValue = variantMapToScriptValue(entityMap, scriptEngine);
EntityItemProperties properties;
EntityItemPropertiesFromScriptValueIgnoreReadOnly(entityScriptValue, properties);
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 86bfe984f6..d8981903d0 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -278,6 +278,8 @@ public:
QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey);
bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce, EntityItemID& id);
+ void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; }
+
signals:
void deletingEntity(const EntityItemID& entityID);
void deletingEntityPointer(EntityItem* entityID);
@@ -383,6 +385,8 @@ private:
void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation);
+
+ std::shared_ptr _myAvatar{ nullptr };
};
#endif // hifi_EntityTree_h
diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.cpp b/libraries/entities/src/RecurseOctreeToMapOperator.cpp
index 217dc0db12..5be921112f 100644
--- a/libraries/entities/src/RecurseOctreeToMapOperator.cpp
+++ b/libraries/entities/src/RecurseOctreeToMapOperator.cpp
@@ -17,13 +17,15 @@ RecurseOctreeToMapOperator::RecurseOctreeToMapOperator(QVariantMap& map,
const OctreeElementPointer& top,
QScriptEngine* engine,
bool skipDefaultValues,
- bool skipThoseWithBadParents) :
+ bool skipThoseWithBadParents,
+ std::shared_ptr myAvatar) :
RecurseOctreeOperator(),
_map(map),
_top(top),
_engine(engine),
_skipDefaultValues(skipDefaultValues),
- _skipThoseWithBadParents(skipThoseWithBadParents)
+ _skipThoseWithBadParents(skipThoseWithBadParents),
+ _myAvatar(myAvatar)
{
// if some element "top" was given, only save information for that element and its children.
if (_top) {
@@ -60,6 +62,18 @@ bool RecurseOctreeToMapOperator::postRecursion(const OctreeElementPointer& eleme
} else {
qScriptValues = EntityItemPropertiesToScriptValue(_engine, properties);
}
+
+ // handle parentJointName for wearables
+ if (_myAvatar && entityItem->getParentID() == AVATAR_SELF_ID &&
+ entityItem->getParentJointIndex() != INVALID_JOINT_INDEX) {
+
+ auto jointNames = _myAvatar->getJointNames();
+ auto parentJointIndex = entityItem->getParentJointIndex();
+ if (parentJointIndex < jointNames.count()) {
+ qScriptValues.setProperty("parentJointName", jointNames.at(parentJointIndex));
+ }
+ }
+
entitiesQList << qScriptValues.toVariant();
});
diff --git a/libraries/entities/src/RecurseOctreeToMapOperator.h b/libraries/entities/src/RecurseOctreeToMapOperator.h
index c661badd88..985ec9de35 100644
--- a/libraries/entities/src/RecurseOctreeToMapOperator.h
+++ b/libraries/entities/src/RecurseOctreeToMapOperator.h
@@ -14,7 +14,7 @@
class RecurseOctreeToMapOperator : public RecurseOctreeOperator {
public:
RecurseOctreeToMapOperator(QVariantMap& map, const OctreeElementPointer& top, QScriptEngine* engine, bool skipDefaultValues,
- bool skipThoseWithBadParents);
+ bool skipThoseWithBadParents, std::shared_ptr myAvatar);
bool preRecursion(const OctreeElementPointer& element) override;
bool postRecursion(const OctreeElementPointer& element) override;
private:
@@ -24,4 +24,5 @@ public:
bool _withinTop;
bool _skipDefaultValues;
bool _skipThoseWithBadParents;
+ std::shared_ptr _myAvatar;
};