// // Line3DOverlay.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 "Line3DOverlay.h" #include #include #include "AbstractViewStateInterface.h" QString const Line3DOverlay::TYPE = "line3d"; Line3DOverlay::Line3DOverlay() : _geometryCacheID(DependencyManager::get()->allocateID()) { } Line3DOverlay::Line3DOverlay(const Line3DOverlay* line3DOverlay) : Base3DOverlay(line3DOverlay), _geometryCacheID(DependencyManager::get()->allocateID()) { setParentID(line3DOverlay->getParentID()); setParentJointIndex(line3DOverlay->getParentJointIndex()); setLocalTransform(line3DOverlay->getLocalTransform()); _direction = line3DOverlay->getDirection(); _length = line3DOverlay->getLength(); _endParentID = line3DOverlay->getEndParentID(); _endParentJointIndex = line3DOverlay->getEndJointIndex(); _lineWidth = line3DOverlay->getLineWidth(); _glow = line3DOverlay->getGlow(); } Line3DOverlay::~Line3DOverlay() { auto geometryCache = DependencyManager::get(); if (_geometryCacheID && geometryCache) { geometryCache->releaseID(_geometryCacheID); } } glm::vec3 Line3DOverlay::getStart() const { return getWorldPosition(); } glm::vec3 Line3DOverlay::getEnd() const { bool success; glm::vec3 localEnd; glm::vec3 worldEnd; if (_endParentID != QUuid()) { glm::vec3 localOffset = _direction * _length; bool success; worldEnd = localToWorld(localOffset, _endParentID, _endParentJointIndex, success); return worldEnd; } localEnd = getLocalEnd(); worldEnd = localToWorld(localEnd, getParentID(), getParentJointIndex(), success); if (!success) { qDebug() << "Line3DOverlay::getEnd failed"; } return worldEnd; } void Line3DOverlay::setStart(const glm::vec3& start) { setWorldPosition(start); } void Line3DOverlay::setEnd(const glm::vec3& end) { bool success; glm::vec3 localStart; glm::vec3 localEnd; glm::vec3 offset; if (_endParentID != QUuid()) { offset = worldToLocal(end, _endParentID, _endParentJointIndex, success); } else { localStart = getLocalStart(); localEnd = worldToLocal(end, getParentID(), getParentJointIndex(), success); offset = localEnd - localStart; } if (!success) { qDebug() << "Line3DOverlay::setEnd failed"; return; } _length = glm::length(offset); if (_length > 0.0f) { _direction = glm::normalize(offset); } else { _direction = glm::vec3(0.0f); } notifyRenderVariableChange(); } void Line3DOverlay::setLocalEnd(const glm::vec3& localEnd) { glm::vec3 offset; if (_endParentID != QUuid()) { offset = localEnd; } else { glm::vec3 localStart = getLocalStart(); offset = localEnd - localStart; } _length = glm::length(offset); if (_length > 0.0f) { _direction = glm::normalize(offset); } else { _direction = glm::vec3(0.0f); } } AABox Line3DOverlay::getBounds() const { auto extents = Extents{}; extents.addPoint(getStart()); extents.addPoint(getEnd()); return AABox(extents); } void Line3DOverlay::render(RenderArgs* args) { if (!_renderVisible) { return; // do nothing if we're not visible } float alpha = getAlpha(); xColor color = getColor(); const float MAX_COLOR = 255.0f; glm::vec4 colorv4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha); auto batch = args->_batch; if (batch) { batch->setModelTransform(Transform()); auto& renderTransform = getRenderTransform(); glm::vec3 start = renderTransform.getTranslation(); glm::vec3 end = renderTransform.transform(vec3(0.0, 0.0, -1.0)); auto geometryCache = DependencyManager::get(); if (getIsDashedLine()) { // TODO: add support for color to renderDashedLine() geometryCache->bindSimpleProgram(*batch, false, false, false, true, true); geometryCache->renderDashedLine(*batch, start, end, colorv4, _geometryCacheID); } else { // renderGlowLine handles both glow = 0 and glow > 0 cases geometryCache->renderGlowLine(*batch, start, end, colorv4, _glow, _lineWidth, _geometryCacheID); } } } const render::ShapeKey Line3DOverlay::getShapeKey() { auto builder = render::ShapeKey::Builder().withOwnPipeline(); if (isTransparent()) { builder.withTranslucent(); } return builder.build(); } void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { QVariantMap properties = originalProperties; glm::vec3 newStart(0.0f); bool newStartSet { false }; glm::vec3 newEnd(0.0f); bool newEndSet { false }; auto start = properties["start"]; // If "start" property was not there, check to see if they included aliases: startPoint, p1 if (!start.isValid()) { start = properties["startPoint"]; } if (!start.isValid()) { start = properties["p1"]; } if (start.isValid()) { newStart = vec3FromVariant(start); newStartSet = true; } properties.remove("start"); // so that Base3DOverlay doesn't respond to it properties.remove("startPoint"); properties.remove("p1"); auto end = properties["end"]; // If "end" property was not there, check to see if they included aliases: endPoint, p2 if (!end.isValid()) { end = properties["endPoint"]; } if (!end.isValid()) { end = properties["p2"]; } if (end.isValid()) { newEnd = vec3FromVariant(end); newEndSet = true; } properties.remove("end"); // so that Base3DOverlay doesn't respond to it properties.remove("endPoint"); properties.remove("p2"); auto length = properties["length"]; if (length.isValid()) { _length = length.toFloat(); } Base3DOverlay::setProperties(properties); auto endParentIDProp = properties["endParentID"]; if (endParentIDProp.isValid()) { _endParentID = QUuid(endParentIDProp.toString()); } auto endParentJointIndexProp = properties["endParentJointIndex"]; if (endParentJointIndexProp.isValid()) { _endParentJointIndex = endParentJointIndexProp.toInt(); } auto localStart = properties["localStart"]; if (localStart.isValid()) { glm::vec3 tmpLocalEnd = getLocalEnd(); setLocalStart(vec3FromVariant(localStart)); setLocalEnd(tmpLocalEnd); } auto localEnd = properties["localEnd"]; if (localEnd.isValid()) { setLocalEnd(vec3FromVariant(localEnd)); } // these are saved until after Base3DOverlay::setProperties so parenting infomation can be set, first if (newStartSet) { setStart(newStart); } if (newEndSet) { setEnd(newEnd); } auto glow = properties["glow"]; if (glow.isValid()) { float prevGlow = _glow; setGlow(glow.toFloat()); // Update our payload key if necessary to handle transparency if ((prevGlow <= 0.0f && _glow > 0.0f) || (prevGlow > 0.0f && _glow <= 0.0f)) { auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::Transaction transaction; transaction.updateItem(itemID); scene->enqueueTransaction(transaction); } } } auto lineWidth = properties["lineWidth"]; if (lineWidth.isValid()) { setLineWidth(lineWidth.toFloat()); } } /**jsdoc * These are the properties of a line3d {@link Overlays.OverlayType|OverlayType}. * @typedef {object} Overlays.Line3DProperties * * @property {string} type=line3d - Has the value "line3d". Read-only. * @property {Color} color=255,255,255 - The color of the overlay. * @property {number} alpha=0.7 - The opacity of the overlay, 0.0 - 1.0. * @property {number} pulseMax=0 - The maximum value of the pulse multiplier. * @property {number} pulseMin=0 - The minimum value of the pulse multiplier. * @property {number} pulsePeriod=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from * pulseMin to pulseMax, then pulseMax to pulseMin in one period. * @property {number} alphaPulse=0 - If non-zero, the alpha of the overlay is pulsed: the alpha value is multiplied by the * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {number} colorPulse=0 - If non-zero, the color of the overlay is pulsed: the color value is multiplied by the * current pulse multiplier value each frame. If > 0 the pulse multiplier is applied in phase with the pulse period; if < 0 * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and * rotating as you move your avatar. * * @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, * filled, and filed. Antonyms: isWire and wire. * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will * be removed. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, * {@link Overlays.findRayIntersection|findRayIntersection} ignores the overlay. * @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 {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". * * @property {Uuid} endParentID=null - The avatar, entity, or overlay that the end point of the line is parented to. * @property {number} endParentJointIndex=65535 - Integer value specifying the skeleton joint that the end point of the line is * attached to if parentID is an avatar skeleton. A value of 65535 means "no joint". * @property {Vec3} start - The start point of the line. Synonyms: startPoint, p1, and * position. * @property {Vec3} end - The end point of the line. Synonyms: endPoint and p2. * @property {Vec3} localStart - The local position of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as start. Synonym: localPosition. * @property {Vec3} localEnd - The local position of the overlay relative to its parent if the overlay has a * endParentID set, otherwise the same value as end. * @property {number} length - The length of the line, in meters. This can be set after creating a line with start and end * points. * @property {number} glow=0 - If glow > 0, the line is rendered with a glow. * @property {number} lineWidth=0.02 - If glow > 0, this is the width of the glow, in meters. */ QVariant Line3DOverlay::getProperty(const QString& property) { if (property == "start" || property == "startPoint" || property == "p1") { return vec3toVariant(getStart()); } if (property == "end" || property == "endPoint" || property == "p2") { return vec3toVariant(getEnd()); } if (property == "length") { return QVariant(getLength()); } if (property == "endParentID") { return _endParentID; } if (property == "endParentJointIndex") { return _endParentJointIndex; } if (property == "localStart") { return vec3toVariant(getLocalStart()); } if (property == "localEnd") { return vec3toVariant(getLocalEnd()); } if (property == "glow") { return getGlow(); } if (property == "lineWidth") { return _lineWidth; } return Base3DOverlay::getProperty(property); } Line3DOverlay* Line3DOverlay::createClone() const { return new Line3DOverlay(this); } Transform Line3DOverlay::evalRenderTransform() { // Capture start and endin the renderTransform: // start is the origin // end is at the tip of the front axis aka -Z Transform transform; transform.setTranslation( getStart()); auto endPos = getEnd(); auto vec = endPos - transform.getTranslation(); const float MIN_LINE_LENGTH = 0.0001f; auto scale = glm::max(glm::length(vec), MIN_LINE_LENGTH); auto dir = vec / scale; auto orientation = glm::rotation(glm::vec3(0.0f, 0.0f, -1.0f), dir); transform.setRotation(orientation); transform.setScale(scale); return transform; }