// // Image3DOverlay.cpp // // // Created by Clement on 7/1/14. // Modified and renamed by Zander Otavka on 8/4/15 // 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 "Image3DOverlay.h" #include #include #include #include #include "GeometryUtil.h" #include "AbstractViewStateInterface.h" QString const Image3DOverlay::TYPE = "image3d"; Image3DOverlay::Image3DOverlay() { _isLoaded = false; _geometryId = DependencyManager::get()->allocateID(); } Image3DOverlay::Image3DOverlay(const Image3DOverlay* image3DOverlay) : Billboard3DOverlay(image3DOverlay), _url(image3DOverlay->_url), _texture(image3DOverlay->_texture), _emissive(image3DOverlay->_emissive), _fromImage(image3DOverlay->_fromImage) { _geometryId = DependencyManager::get()->allocateID(); } Image3DOverlay::~Image3DOverlay() { auto geometryCache = DependencyManager::get(); if (geometryCache) { geometryCache->releaseID(_geometryId); } } void Image3DOverlay::update(float deltatime) { if (!_isLoaded) { _isLoaded = true; _texture = DependencyManager::get()->getTexture(_url); _textureIsLoaded = false; } #if OVERLAY_PANELS if (usecTimestampNow() > _transformExpiry) { Transform transform = getTransform(); applyTransformTo(transform); setTransform(transform); } #endif Parent::update(deltatime); } void Image3DOverlay::render(RenderArgs* args) { if (!_visible || !getParentVisible() || !_texture || !_texture->isLoaded()) { return; } // Once the texture has loaded, check if we need to update the render item because of transparency if (!_textureIsLoaded && _texture && _texture->getGPUTexture()) { _textureIsLoaded = true; bool prevAlphaTexture = _alphaTexture; _alphaTexture = _texture->getGPUTexture()->getUsage().isAlpha(); if (_alphaTexture != prevAlphaTexture) { auto itemID = getRenderItemID(); if (render::Item::isValidID(itemID)) { render::ScenePointer scene = AbstractViewStateInterface::instance()->getMain3DScene(); render::Transaction transaction; transaction.updateItem(itemID); scene->enqueueTransaction(transaction); } } } Q_ASSERT(args->_batch); gpu::Batch* batch = args->_batch; float imageWidth = _texture->getWidth(); float imageHeight = _texture->getHeight(); QRect fromImage; if (_fromImage.isNull()) { fromImage.setX(0); fromImage.setY(0); fromImage.setWidth(imageWidth); fromImage.setHeight(imageHeight); } else { float scaleX = imageWidth / _texture->getOriginalWidth(); float scaleY = imageHeight / _texture->getOriginalHeight(); fromImage.setX(scaleX * _fromImage.x()); fromImage.setY(scaleY * _fromImage.y()); fromImage.setWidth(scaleX * _fromImage.width()); fromImage.setHeight(scaleY * _fromImage.height()); } float maxSize = glm::max(fromImage.width(), fromImage.height()); float x = fromImage.width() / (2.0f * maxSize); float y = -fromImage.height() / (2.0f * maxSize); glm::vec2 topLeft(-x, -y); glm::vec2 bottomRight(x, y); glm::vec2 texCoordTopLeft((fromImage.x() + 0.5f) / imageWidth, (fromImage.y() + 0.5f) / imageHeight); glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth, (fromImage.y() + fromImage.height() - 0.5f) / imageHeight); const float MAX_COLOR = 255.0f; xColor color = getColor(); float alpha = getAlpha(); batch->setModelTransform(getRenderTransform()); batch->setResourceTexture(0, _texture->getGPUTexture()); DependencyManager::get()->renderQuad( *batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight, glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha), _geometryId ); batch->setResourceTexture(0, nullptr); // restore default white color after me } const render::ShapeKey Image3DOverlay::getShapeKey() { auto builder = render::ShapeKey::Builder().withoutCullFace().withDepthBias(); if (_emissive) { builder.withUnlit(); } if (isTransparent()) { builder.withTranslucent(); } return builder.build(); } void Image3DOverlay::setProperties(const QVariantMap& properties) { Billboard3DOverlay::setProperties(properties); auto urlValue = properties["url"]; if (urlValue.isValid()) { QString newURL = urlValue.toString(); if (newURL != _url) { setURL(newURL); } } auto subImageBoundsVar = properties["subImage"]; if (subImageBoundsVar.isValid()) { if (subImageBoundsVar.isNull()) { _fromImage = QRect(); } else { QRect oldSubImageRect = _fromImage; QRect subImageRect = _fromImage; auto subImageBounds = subImageBoundsVar.toMap(); if (subImageBounds["x"].isValid()) { subImageRect.setX(subImageBounds["x"].toInt()); } else { subImageRect.setX(oldSubImageRect.x()); } if (subImageBounds["y"].isValid()) { subImageRect.setY(subImageBounds["y"].toInt()); } else { subImageRect.setY(oldSubImageRect.y()); } if (subImageBounds["width"].isValid()) { subImageRect.setWidth(subImageBounds["width"].toInt()); } else { subImageRect.setWidth(oldSubImageRect.width()); } if (subImageBounds["height"].isValid()) { subImageRect.setHeight(subImageBounds["height"].toInt()); } else { subImageRect.setHeight(oldSubImageRect.height()); } setClipFromSource(subImageRect); } } auto emissiveValue = properties["emissive"]; if (emissiveValue.isValid()) { _emissive = emissiveValue.toBool(); } } QVariant Image3DOverlay::getProperty(const QString& property) { if (property == "url") { return _url; } if (property == "subImage") { return _fromImage; } if (property == "offsetPosition") { return vec3toVariant(getOffsetPosition()); } if (property == "emissive") { return _emissive; } return Billboard3DOverlay::getProperty(property); } void Image3DOverlay::setURL(const QString& url) { _url = url; _isLoaded = false; } bool Image3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { if (_texture && _texture->isLoaded()) { // Make sure position and rotation is updated. Transform transform = getTransform(); // Don't call applyTransformTo() or setTransform() here because this code runs too frequently. // Produce the dimensions of the overlay based on the image's aspect ratio and the overlay's scale. bool isNull = _fromImage.isNull(); float width = isNull ? _texture->getWidth() : _fromImage.width(); float height = isNull ? _texture->getHeight() : _fromImage.height(); float maxSize = glm::max(width, height); glm::vec2 dimensions = _dimensions * glm::vec2(width / maxSize, height / maxSize); // FIXME - face and surfaceNormal not being set return findRayRectangleIntersection(origin, direction, transform.getRotation(), transform.getTranslation(), dimensions, distance); } return false; } Image3DOverlay* Image3DOverlay::createClone() const { return new Image3DOverlay(this); } Transform Image3DOverlay::evalRenderTransform() { auto transform = Parent::evalRenderTransform(); transform.postScale(glm::vec3(getDimensions(), 1.0f)); return transform; }