// // Base3DOverlay.cpp // interface/src/ui/overlays // // Copyright 2014 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 "Base3DOverlay.h" #include #include #include "Application.h" const bool DEFAULT_IS_SOLID = false; const bool DEFAULT_IS_DASHED_LINE = false; Base3DOverlay::Base3DOverlay() : SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), _isSolid(DEFAULT_IS_SOLID), _isDashedLine(DEFAULT_IS_DASHED_LINE), _ignorePickIntersection(false), _drawInFront(false), _drawHUDLayer(false) { } Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) : Overlay(base3DOverlay), SpatiallyNestable(NestableType::Overlay, QUuid::createUuid()), _isSolid(base3DOverlay->_isSolid), _isDashedLine(base3DOverlay->_isDashedLine), _ignorePickIntersection(base3DOverlay->_ignorePickIntersection), _drawInFront(base3DOverlay->_drawInFront), _drawHUDLayer(base3DOverlay->_drawHUDLayer), _isGrabbable(base3DOverlay->_isGrabbable), _isVisibleInSecondaryCamera(base3DOverlay->_isVisibleInSecondaryCamera) { setTransform(base3DOverlay->getTransform()); } QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& properties, bool scalesWithParent) { // the position and rotation in _transform are relative to the parent (aka local). The versions coming from // scripts are in world-frame, unless localPosition or localRotation are used. Patch up the properties // so that "position" and "rotation" are relative-to-parent values. QVariantMap result = properties; QUuid parentID = result["parentID"].isValid() ? QUuid(result["parentID"].toString()) : QUuid(); int parentJointIndex = result["parentJointIndex"].isValid() ? result["parentJointIndex"].toInt() : -1; bool success; // make "position" and "orientation" be relative-to-parent if (result["localPosition"].isValid()) { result["position"] = result["localPosition"]; } else if (result["position"].isValid()) { glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]), parentID, parentJointIndex, scalesWithParent, success); if (success) { result["position"] = vec3toVariant(localPosition); } } if (result["localOrientation"].isValid()) { result["orientation"] = result["localOrientation"]; } else if (result["orientation"].isValid()) { glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]), parentID, parentJointIndex, scalesWithParent, success); if (success) { result["orientation"] = quatToVariant(localOrientation); } } return result; } void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { QVariantMap properties = originalProperties; if (properties["name"].isValid()) { setName(properties["name"].toString()); } // carry over some legacy keys if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { if (properties["p1"].isValid()) { properties["position"] = properties["p1"]; } else if (properties["point"].isValid()) { properties["position"] = properties["point"]; } else if (properties["start"].isValid()) { properties["position"] = properties["start"]; } } if (!properties["orientation"].isValid() && properties["rotation"].isValid()) { properties["orientation"] = properties["rotation"]; } if (!properties["localOrientation"].isValid() && properties["localRotation"].isValid()) { properties["localOrientation"] = properties["localRotation"]; } // All of parentID, parentJointIndex, position, orientation are needed to make sense of any of them. // If any of these changed, pull any missing properties from the overlay. if (properties["parentID"].isValid() || properties["parentJointIndex"].isValid() || properties["position"].isValid() || properties["localPosition"].isValid() || properties["orientation"].isValid() || properties["localOrientation"].isValid()) { if (!properties["parentID"].isValid()) { properties["parentID"] = getParentID(); } if (!properties["parentJointIndex"].isValid()) { properties["parentJointIndex"] = getParentJointIndex(); } if (!properties["position"].isValid() && !properties["localPosition"].isValid()) { properties["position"] = vec3toVariant(getWorldPosition()); } if (!properties["orientation"].isValid() && !properties["localOrientation"].isValid()) { properties["orientation"] = quatToVariant(getWorldOrientation()); } } properties = convertOverlayLocationFromScriptSemantics(properties, getScalesWithParent()); Overlay::setProperties(properties); bool needRenderItemUpdate = false; auto drawInFront = properties["drawInFront"]; if (drawInFront.isValid()) { bool value = drawInFront.toBool(); setDrawInFront(value); needRenderItemUpdate = true; } auto drawHUDLayer = properties["drawHUDLayer"]; if (drawHUDLayer.isValid()) { bool value = drawHUDLayer.toBool(); setDrawHUDLayer(value); needRenderItemUpdate = true; } auto isGrabbable = properties["grabbable"]; if (isGrabbable.isValid()) { setIsGrabbable(isGrabbable.toBool()); } auto isVisibleInSecondaryCamera = properties["isVisibleInSecondaryCamera"]; if (isVisibleInSecondaryCamera.isValid()) { bool value = isVisibleInSecondaryCamera.toBool(); setIsVisibleInSecondaryCamera(value); needRenderItemUpdate = true; } if (properties["position"].isValid()) { setLocalPosition(vec3FromVariant(properties["position"])); needRenderItemUpdate = true; } if (properties["orientation"].isValid()) { setLocalOrientation(quatFromVariant(properties["orientation"])); needRenderItemUpdate = true; } if (properties["isSolid"].isValid()) { setIsSolid(properties["isSolid"].toBool()); } if (properties["isFilled"].isValid()) { setIsSolid(properties["isSolid"].toBool()); } if (properties["isWire"].isValid()) { setIsSolid(!properties["isWire"].toBool()); } if (properties["solid"].isValid()) { setIsSolid(properties["solid"].toBool()); } if (properties["filled"].isValid()) { setIsSolid(properties["filled"].toBool()); } if (properties["wire"].isValid()) { setIsSolid(!properties["wire"].toBool()); } if (properties["isDashedLine"].isValid()) { setIsDashedLine(properties["isDashedLine"].toBool()); } if (properties["dashed"].isValid()) { setIsDashedLine(properties["dashed"].toBool()); } if (properties["ignorePickIntersection"].isValid()) { setIgnorePickIntersection(properties["ignorePickIntersection"].toBool()); } else if (properties["ignoreRayIntersection"].isValid()) { setIgnorePickIntersection(properties["ignoreRayIntersection"].toBool()); } if (properties["parentID"].isValid()) { setParentID(QUuid(properties["parentID"].toString())); bool success; getParentPointer(success); // call this to hook-up the parent's back-pointers to its child overlays needRenderItemUpdate = true; } if (properties["parentJointIndex"].isValid()) { setParentJointIndex(properties["parentJointIndex"].toInt()); needRenderItemUpdate = true; } // Communicate changes to the renderItem if needed if (needRenderItemUpdate) { auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; transaction.updateItem(itemID); scene->enqueueTransaction(transaction); } } } // JSDoc for copying to @typedefs of overlay types that inherit Base3DOverlay. /**jsdoc * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and * start. * @property {Vec3} localPosition - The local position of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as position. * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignorePickIntersection=false - If true, picks ignore the overlay. ignoreRayIntersection is a synonym. * @property {boolean} drawInFront=false - If true, the overlay is rendered in front of other overlays that don't * have drawInFront set to true, and in front of entities. * @property {boolean} grabbable=false - Signal to grabbing scripts whether or not this overlay can be grabbed. * @property {boolean} isVisibleInSecondaryCamera=false - If true, the overlay is rendered in secondary * camera views. * @property {Uuid} parentID=null - The avatar, entity, or overlay that the overlay is parented to. * @property {number} parentJointIndex=65535 - Integer value specifying the skeleton joint that the overlay is attached to if * parentID is an avatar skeleton. A value of 65535 means "no joint". */ QVariant Base3DOverlay::getProperty(const QString& property) { if (property == "name") { return _nameLock.resultWithReadLock([&] { return _name; }); } if (property == "position" || property == "start" || property == "p1" || property == "point") { return vec3toVariant(getWorldPosition()); } if (property == "localPosition") { return vec3toVariant(getLocalPosition()); } if (property == "rotation" || property == "orientation") { return quatToVariant(getWorldOrientation()); } if (property == "localRotation" || property == "localOrientation") { return quatToVariant(getLocalOrientation()); } if (property == "isSolid" || property == "isFilled" || property == "solid" || property == "filled") { return _isSolid; } if (property == "isWire" || property == "wire") { return !_isSolid; } if (property == "isDashedLine" || property == "dashed") { return _isDashedLine; } if (property == "ignorePickIntersection" || property == "ignoreRayIntersection") { return _ignorePickIntersection; } if (property == "drawInFront") { return _drawInFront; } if (property == "grabbable") { return _isGrabbable; } if (property == "isVisibleInSecondaryCamera") { return _isVisibleInSecondaryCamera; } if (property == "parentID") { return getParentID(); } if (property == "parentJointIndex") { return getParentJointIndex(); } return Overlay::getProperty(property); } void Base3DOverlay::locationChanged(bool tellPhysics) { SpatiallyNestable::locationChanged(tellPhysics); // Force the actual update of the render transform through the notify call notifyRenderVariableChange(); } void Base3DOverlay::parentDeleted() { qApp->getOverlays().deleteOverlay(getOverlayID()); } void Base3DOverlay::update(float duration) { // In Base3DOverlay, if its location or bound changed, the renderTrasnformDirty flag is true. // then the correct transform used for rendering is computed in the update transaction and assigned. if (_renderVariableDirty) { auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { // Capture the render transform value in game loop before auto latestTransform = evalRenderTransform(); bool latestVisible = getVisible(); _renderVariableDirty = false; render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; transaction.updateItem(itemID, [latestTransform, latestVisible](Overlay& data) { auto overlay3D = dynamic_cast(&data); if (overlay3D) { // TODO: overlays need to communicate all relavent render properties through transactions overlay3D->setRenderTransform(latestTransform); overlay3D->setRenderVisible(latestVisible); } }); scene->enqueueTransaction(transaction); } } } void Base3DOverlay::notifyRenderVariableChange() const { _renderVariableDirty = true; } Transform Base3DOverlay::evalRenderTransform() { return getTransform(); } void Base3DOverlay::setRenderTransform(const Transform& transform) { _renderTransform = transform; } void Base3DOverlay::setRenderVisible(bool visible) { _renderVisible = visible; } SpatialParentTree* Base3DOverlay::getParentTree() const { auto entityTreeRenderer = qApp->getEntities(); EntityTreePointer entityTree = entityTreeRenderer ? entityTreeRenderer->getTree() : nullptr; return entityTree.get(); } void Base3DOverlay::setVisible(bool visible) { Parent::setVisible(visible); notifyRenderVariableChange(); } QString Base3DOverlay::getName() const { return _nameLock.resultWithReadLock([&] { return QString("Overlay:") + _name; }); } void Base3DOverlay::setName(QString name) { _nameLock.withWriteLock([&] { _name = name; }); } render::ItemKey Base3DOverlay::getKey() { auto builder = render::ItemKey::Builder(Overlay::getKey()); if (getDrawInFront()) { builder.withLayer(render::hifi::LAYER_3D_FRONT); } else if (getDrawHUDLayer()) { builder.withLayer(render::hifi::LAYER_3D_HUD); } else { builder.withoutLayer(); } builder.withoutViewSpace(); if (isTransparent()) { builder.withTransparent(); } return builder.build(); }