diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index dec4af2d93..5fb5a15d2c 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -15,6 +15,7 @@ #include #include "RenderableLightEntityItem.h" +#include "RenderableLineEntityItem.h" #include "RenderableModelEntityItem.h" #include "RenderableParticleEffectEntityItem.h" #include "RenderablePolyVoxEntityItem.h" @@ -247,6 +248,10 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer, result = make_renderer(entity); break; + case Type::Line: + result = make_renderer(entity); + break; + case Type::PolyLine: result = make_renderer(entity); break; diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.cpp b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp new file mode 100644 index 0000000000..9c5424950a --- /dev/null +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.cpp @@ -0,0 +1,61 @@ +// +// RenderableLineEntityItem.cpp +// libraries/entities-renderer/src/ +// +// Created by Seth Alves on 5/11/15. +// Copyright 2015 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 "RenderableLineEntityItem.h" + +#include +#include + +using namespace render; +using namespace render::entities; + +void LineEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) { + if (_lineVerticesID != GeometryCache::UNKNOWN_ID) { + auto geometryCache = DependencyManager::get(); + if (geometryCache) { + geometryCache->releaseID(_lineVerticesID); + } + } +} + +bool LineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { + return entity->pointsChanged(); +} + +void LineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { + entity->resetPointsChanged(); + _linePoints = entity->getLinePoints(); + auto geometryCache = DependencyManager::get(); + if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { + _lineVerticesID = geometryCache->allocateID(); + } + glm::vec4 lineColor(toGlm(entity->getColor()), 1.0f); + geometryCache->updateVertices(_lineVerticesID, _linePoints, lineColor); +} + +void LineEntityRenderer::doRender(RenderArgs* args) { + if (_lineVerticesID == GeometryCache::UNKNOWN_ID) { + return; + } + + PerformanceTimer perfTimer("RenderableLineEntityItem::render"); + Q_ASSERT(args->_batch); + gpu::Batch& batch = *args->_batch; + const auto& modelTransform = getModelTransform(); + Transform transform = Transform(); + transform.setTranslation(modelTransform.getTranslation()); + transform.setRotation(modelTransform.getRotation()); + batch.setModelTransform(transform); + if (_linePoints.size() > 1) { + DependencyManager::get()->bindSimpleProgram(batch); + DependencyManager::get()->renderVertices(batch, gpu::LINE_STRIP, _lineVerticesID); + } +} diff --git a/libraries/entities-renderer/src/RenderableLineEntityItem.h b/libraries/entities-renderer/src/RenderableLineEntityItem.h new file mode 100644 index 0000000000..799ba5b78c --- /dev/null +++ b/libraries/entities-renderer/src/RenderableLineEntityItem.h @@ -0,0 +1,42 @@ +// +// RenderableLineEntityItem.h +// libraries/entities-renderer/src/ +// +// Created by Seth Alves on 5/11/15. +// Copyright 2015 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 +// + +#ifndef hifi_RenderableLineEntityItem_h +#define hifi_RenderableLineEntityItem_h + +#include "RenderableEntityItem.h" +#include +#include + + +namespace render { namespace entities { + +class LineEntityRenderer : public TypedEntityRenderer { + using Parent = TypedEntityRenderer; + friend class EntityRenderer; + +public: + LineEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { } + +protected: + virtual void onRemoveFromSceneTyped(const TypedEntityPointer& entity) override; + virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; + virtual void doRender(RenderArgs* args) override; + +private: + int _lineVerticesID { GeometryCache::UNKNOWN_ID }; + QVector _linePoints; +}; + +} } // namespace + +#endif // hifi_RenderableLineEntityItem_h diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 4a665bf666..0d9e948db8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -19,35 +19,66 @@ #include #include -#include "paintStroke_shared.slh" +//#define POLYLINE_ENTITY_USE_FADE_EFFECT +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT +# include +#endif using namespace render; using namespace render::entities; -gpu::PipelinePointer PolyLineEntityRenderer::_pipeline = nullptr; +static uint8_t CUSTOM_PIPELINE_NUMBER { 0 }; +static const int32_t PAINTSTROKE_TEXTURE_SLOT { 0 }; +static gpu::Stream::FormatPointer polylineFormat; +static gpu::PipelinePointer polylinePipeline; +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT +static gpu::PipelinePointer polylineFadePipeline; +#endif -static const QUrl DEFAULT_POLYLINE_TEXTURE = QUrl(PathUtils::resourcesPath() + "images/paintStroke.png"); +static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) { + if (!polylinePipeline) { + gpu::ShaderPointer program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke); +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT + auto fadeVS = gpu::Shader::createVertex(std::string(paintStroke_fade_vert)); + auto fadePS = gpu::Shader::createPixel(std::string(paintStroke_fade_frag)); + gpu::ShaderPointer fadeProgram = gpu::Shader::createProgram(fadeVS, fadePS); +#endif + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(true, true, gpu::LESS_EQUAL); + PrepareStencil::testMask(*state); + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); + polylinePipeline = gpu::Pipeline::create(program, state); +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT + _fadePipeline = gpu::Pipeline::create(fadeProgram, state); +#endif + } -PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { - _texture = DependencyManager::get()->getTexture(DEFAULT_POLYLINE_TEXTURE); - - _polylineGeometryBuffer = std::make_shared(); - - _polylineDataBuffer = std::make_shared(); - _polylineDataBuffer->resize(sizeof(PolylineData)); +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT + if (key.isFaded()) { + auto fadeEffect = DependencyManager::get(); + return std::make_shared(_fadePipeline, nullptr, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); + } else { +#endif + return std::make_shared(polylinePipeline, nullptr, nullptr, nullptr); +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT + } +#endif } -void PolyLineEntityRenderer::buildPipeline() { - // FIXME: opaque pipeline - gpu::ShaderPointer program = gpu::Shader::createProgram(shader::entities_renderer::program::paintStroke); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setCullMode(gpu::State::CullMode::CULL_NONE); - state->setDepthTest(true, true, gpu::LESS_EQUAL); - PrepareStencil::testMask(*state); - state->setBlendFunction(true, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::FACTOR_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ONE); - _pipeline = gpu::Pipeline::create(program, state); +PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { + static std::once_flag once; + std::call_once(once, [&] { + CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(shapePipelineFactory); + polylineFormat.reset(new gpu::Stream::Format()); + polylineFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), offsetof(Vertex, position)); + polylineFormat->setAttribute(gpu::Stream::NORMAL, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ), offsetof(Vertex, normal)); + polylineFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv)); + polylineFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB), offsetof(Vertex, color)); + }); + + _verticesBuffer = std::make_shared(); } ItemKey PolyLineEntityRenderer::getKey() { @@ -55,165 +86,152 @@ ItemKey PolyLineEntityRenderer::getKey() { } ShapeKey PolyLineEntityRenderer::getShapeKey() { - return ShapeKey::Builder().withOwnPipeline().withTranslucent().withoutCullFace(); -} - -bool PolyLineEntityRenderer::needsRenderUpdate() const { - bool textureLoadedChanged = resultWithReadLock([&] { - return (!_textureLoaded && _texture && _texture->isLoaded()); - }); - - if (textureLoadedChanged) { - return true; - } - - return Parent::needsRenderUpdate(); + return ShapeKey::Builder().withCustom(CUSTOM_PIPELINE_NUMBER).build(); } bool PolyLineEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { return ( entity->pointsChanged() || - entity->widthsChanged() || + entity->strokeWidthsChanged() || entity->normalsChanged() || entity->texturesChanged() || - entity->colorsChanged() || - _isUVModeStretch != entity->getIsUVModeStretch() || - _glow != entity->getGlow() || - _faceCamera != entity->getFaceCamera() + entity->strokeColorsChanged() ); } -void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { - auto pointsChanged = entity->pointsChanged(); - auto widthsChanged = entity->widthsChanged(); - auto normalsChanged = entity->normalsChanged(); - auto colorsChanged = entity->colorsChanged(); - - bool isUVModeStretch = entity->getIsUVModeStretch(); - bool glow = entity->getGlow(); - bool faceCamera = entity->getFaceCamera(); - - entity->resetPolyLineChanged(); - - // Transform - _renderTransform = Transform(); - _renderTransform.setTranslation(entity->getWorldPosition() - entity->getPolylineCenter()); - _renderTransform.setRotation(entity->getWorldOrientation()); - - // Textures +void PolyLineEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { + static const QUrl DEFAULT_POLYLINE_TEXTURE = QUrl(PathUtils::resourcesPath() + "images/paintStroke.png"); + QUrl entityTextures = DEFAULT_POLYLINE_TEXTURE; if (entity->texturesChanged()) { entity->resetTexturesChanged(); - QUrl entityTextures = DEFAULT_POLYLINE_TEXTURE; auto textures = entity->getTextures(); if (!textures.isEmpty()) { entityTextures = QUrl(textures); } _texture = DependencyManager::get()->getTexture(entityTextures); - _textureAspectRatio = 1.0f; - _textureLoaded = false; } - - bool textureChanged = false; - if (!_textureLoaded && _texture && _texture->isLoaded()) { - textureChanged = true; - _textureAspectRatio = (float)_texture->getOriginalHeight() / (float)_texture->getOriginalWidth(); - _textureLoaded = true; - } - - // Geometry - if (pointsChanged) { - _points = entity->getLinePoints(); - } - if (widthsChanged) { - _widths = entity->getStrokeWidths(); - } - if (normalsChanged) { - _normals = entity->getNormals(); - } - if (colorsChanged) { - _colors = entity->getStrokeColors(); - _color = toGlm(entity->getColor()); - } - if (_isUVModeStretch != isUVModeStretch || pointsChanged || widthsChanged || normalsChanged || colorsChanged || textureChanged) { - _isUVModeStretch = isUVModeStretch; - updateGeometry(); - } - - // Data - if (faceCamera != _faceCamera || glow != _glow) { - _faceCamera = faceCamera; - _glow = glow; - updateData(); + + + if (!_texture) { + _texture = DependencyManager::get()->getTexture(entityTextures); } } -void PolyLineEntityRenderer::updateGeometry() { - int maxNumVertices = std::min(_points.length(), _normals.length()); +void PolyLineEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { + auto pointsChanged = entity->pointsChanged(); + auto strokeWidthsChanged = entity->strokeWidthsChanged(); + auto normalsChanged = entity->normalsChanged(); + auto strokeColorsChanged = entity->strokeColorsChanged(); + + bool isUVModeStretch = entity->getIsUVModeStretch(); + entity->resetPolyLineChanged(); + + _polylineTransform = Transform(); + _polylineTransform.setTranslation(entity->getWorldPosition()); + _polylineTransform.setRotation(entity->getWorldOrientation()); + + if (pointsChanged) { + _lastPoints = entity->getLinePoints(); + } + if (strokeWidthsChanged) { + _lastStrokeWidths = entity->getStrokeWidths(); + } + if (normalsChanged) { + _lastNormals = entity->getNormals(); + } + if (strokeColorsChanged) { + _lastStrokeColors = entity->getStrokeColors(); + _lastStrokeColors = _lastNormals.size() == _lastStrokeColors.size() ? _lastStrokeColors : QVector({ toGlm(entity->getColor()) }); + } + if (pointsChanged || strokeWidthsChanged || normalsChanged || strokeColorsChanged) { + _empty = std::min(_lastPoints.size(), std::min(_lastNormals.size(), _lastStrokeWidths.size())) < 2; + if (!_empty) { + updateGeometry(updateVertices(_lastPoints, _lastNormals, _lastStrokeWidths, _lastStrokeColors, isUVModeStretch, _textureAspectRatio)); + } + } +} + +void PolyLineEntityRenderer::updateGeometry(const std::vector& vertices) { + _numVertices = (uint32_t)vertices.size(); + auto bufferSize = _numVertices * sizeof(Vertex); + if (bufferSize > _verticesBuffer->getSize()) { + _verticesBuffer->resize(bufferSize); + } + _verticesBuffer->setSubData(0, vertices); +} + +std::vector PolyLineEntityRenderer::updateVertices(const QVector& points, + const QVector& normals, + const QVector& strokeWidths, + const QVector& strokeColors, + const bool isUVModeStretch, + const float textureAspectRatio) { + // Calculate the minimum vector size out of normals, points, and stroke widths + int size = std::min(points.size(), std::min(normals.size(), strokeWidths.size())); + + std::vector vertices; + + // Guard against an empty polyline + if (size <= 0) { + return vertices; + } + + float uCoordInc = 1.0f / size; + float uCoord = 0.0f; + int finalIndex = size - 1; + glm::vec3 binormal; + float accumulatedDistance = 0.0f; + float distanceToLastPoint = 0.0f; + float accumulatedStrokeWidth = 0.0f; + float strokeWidth = 0.0f; bool doesStrokeWidthVary = false; - if (_widths.size() >= 0) { - for (int i = 1; i < maxNumVertices; i++) { - float width = PolyLineEntityItem::DEFAULT_LINE_WIDTH; - if (i < _widths.length()) { - width = _widths[i]; - } - if (width != _widths[i - 1]) { - doesStrokeWidthVary = true; - break; - } + + + for (int i = 1; i < strokeWidths.size(); i++) { + if (strokeWidths[i] != strokeWidths[i - 1]) { + doesStrokeWidthVary = true; + break; } } - float uCoordInc = 1.0f / maxNumVertices; - float uCoord = 0.0f; - float accumulatedDistance = 0.0f; - float accumulatedStrokeWidth = 0.0f; - glm::vec3 binormal; + for (int i = 0; i <= finalIndex; i++) { + const float& width = strokeWidths.at(i); + const auto& point = points.at(i); + const auto& normal = normals.at(i); + const auto& color = strokeColors.size() == normals.size() ? strokeColors.at(i) : strokeColors.at(0); + int vertexIndex = i * 2; + - std::vector vertices; - vertices.reserve(maxNumVertices); - for (int i = 0; i < maxNumVertices; i++) { - // Position - glm::vec3 point = _points[i]; + if (!isUVModeStretch && i >= 1) { + distanceToLastPoint = glm::distance(points.at(i), points.at(i - 1)); + accumulatedDistance += distanceToLastPoint; + strokeWidth = 2 * strokeWidths[i]; - // uCoord - float width = i < _widths.size() ? _widths[i] : PolyLineEntityItem::DEFAULT_LINE_WIDTH; - if (i > 0) { // First uCoord is 0.0f - if (!_isUVModeStretch) { - accumulatedDistance += glm::distance(point, _points[i - 1]); - - if (doesStrokeWidthVary) { - //If the stroke varies along the line the texture will stretch more or less depending on the speed - //because it looks better than using the same method as below - accumulatedStrokeWidth += width; - float increaseValue = 1; - if (accumulatedStrokeWidth != 0) { - float newUcoord = glm::ceil((_textureAspectRatio * accumulatedDistance) / (accumulatedStrokeWidth / i)); - increaseValue = newUcoord - uCoord; - } - - increaseValue = increaseValue > 0 ? increaseValue : 1; - uCoord += increaseValue; - } else { - // If the stroke width is constant then the textures should keep the aspect ratio along the line - uCoord = (_textureAspectRatio * accumulatedDistance) / width; + if (doesStrokeWidthVary) { + //If the stroke varies along the line the texture will stretch more or less depending on the speed + //because it looks better than using the same method as below + accumulatedStrokeWidth += strokeWidth; + float increaseValue = 1; + if (accumulatedStrokeWidth != 0) { + float newUcoord = glm::ceil(((1.0f / textureAspectRatio) * accumulatedDistance) / (accumulatedStrokeWidth / i)); + increaseValue = newUcoord - uCoord; } + + increaseValue = increaseValue > 0 ? increaseValue : 1; + uCoord += increaseValue; } else { - uCoord += uCoordInc; + //If the stroke width is constant then the textures should keep the aspect ratio along the line + uCoord = ((1.0f / textureAspectRatio) * accumulatedDistance) / strokeWidth; } + } else if (vertexIndex >= 2) { + uCoord += uCoordInc; } - // Color - glm::vec3 color = i < _colors.length() ? _colors[i] : _color; - - // Normal - glm::vec3 normal = _normals[i]; - - // Binormal // For last point we can assume binormals are the same since it represents the last two vertices of quad - if (i < maxNumVertices - 1) { - glm::vec3 tangent = _points[i + 1] - point; - binormal = glm::normalize(glm::cross(tangent, normal)); + if (i < finalIndex) { + const auto tangent = points.at(i + 1) - point; + binormal = glm::normalize(glm::cross(tangent, normal)) * width; // Check to make sure binormal is not a NAN. If it is, don't add to vertices vector if (binormal.x != binormal.x) { @@ -221,36 +239,54 @@ void PolyLineEntityRenderer::updateGeometry() { } } - PolylineVertex vertex = { glm::vec4(point, uCoord), glm::vec4(color, 1.0f), glm::vec4(normal, 0.0f), glm::vec4(binormal, 0.5f * width) }; - vertices.push_back(vertex); + const auto v1 = points.at(i) + binormal; + const auto v2 = points.at(i) - binormal; + vertices.emplace_back(v1, normal, vec2(uCoord, 0.0f), color); + vertices.emplace_back(v2, normal, vec2(uCoord, 1.0f), color); } - _numVertices = vertices.size(); - _polylineGeometryBuffer->setData(vertices.size() * sizeof(PolylineVertex), (const gpu::Byte*) vertices.data()); + return vertices; } -void PolyLineEntityRenderer::updateData() { - PolylineData data { glm::vec2(_faceCamera, _glow), glm::vec2(0.0f) }; - _polylineDataBuffer->setSubData(0, data); +scriptable::ScriptableModelBase PolyLineEntityRenderer::getScriptableModel() { + // TODO: adapt polyline into a triangles mesh... + return EntityRenderer::getScriptableModel(); } void PolyLineEntityRenderer::doRender(RenderArgs* args) { - if (_numVertices < 2) { + if (_empty) { return; } PerformanceTimer perfTimer("RenderablePolyLineEntityItem::render"); Q_ASSERT(args->_batch); - gpu::Batch& batch = *args->_batch; - if (!_pipeline) { - buildPipeline(); + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(_polylineTransform); + + if (_texture && _texture->isLoaded()) { + batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, _texture->getGPUTexture()); + } else { + batch.setResourceTexture(PAINTSTROKE_TEXTURE_SLOT, DependencyManager::get()->getWhiteTexture()); } - batch.setPipeline(_pipeline); - batch.setModelTransform(_renderTransform); - batch.setResourceTexture(0, _textureLoaded ? _texture->getGPUTexture() : DependencyManager::get()->getWhiteTexture()); - batch.setResourceBuffer(0, _polylineGeometryBuffer); - batch.setUniformBuffer(0, _polylineDataBuffer); - batch.draw(gpu::TRIANGLE_STRIP, 2 * _numVertices, 0); + float textureWidth = (float)_texture->getOriginalWidth(); + float textureHeight = (float)_texture->getOriginalHeight(); + if (textureWidth != 0 && textureHeight != 0) { + _textureAspectRatio = textureWidth / textureHeight; + } + + batch.setInputFormat(polylineFormat); + batch.setInputBuffer(0, _verticesBuffer, 0, sizeof(Vertex)); + +#ifndef POLYLINE_ENTITY_USE_FADE_EFFECT + // glColor4f must be called after setInputFormat if it must be taken into account + if (_isFading) { + batch._glColor4f(1.0f, 1.0f, 1.0f, Interpolate::calculateFadeRatio(_fadeStartTime)); + } else { + batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + } +#endif + + batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 635a456ac9..8130171da8 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -25,40 +25,52 @@ class PolyLineEntityRenderer : public TypedEntityRenderer { public: PolyLineEntityRenderer(const EntityItemPointer& entity); - // FIXME: shouldn't always be transparent: take into account texture and glow - virtual bool isTransparent() const override { return true; } - + virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: - virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; + virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, + Transaction& transaction, + const TypedEntityPointer& entity) override; virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override; virtual ItemKey getKey() override; virtual ShapeKey getShapeKey() override; virtual void doRender(RenderArgs* args) override; - void buildPipeline(); - void updateGeometry(); - void updateData(); + virtual bool isTransparent() const override { return true; } - QVector _points; - QVector _normals; - QVector _colors; - glm::vec3 _color; - QVector _widths; + struct Vertex { + Vertex() {} + Vertex(const vec3& position, const vec3& normal, const vec2& uv, const vec3& color) : position(position), + normal(normal), + uv(uv), + color(color) {} + vec3 position; + vec3 normal; + vec2 uv; + vec3 color; + }; + void updateGeometry(const std::vector& vertices); + static std::vector updateVertices(const QVector& points, + const QVector& normals, + const QVector& strokeWidths, + const QVector& strokeColors, + const bool isUVModeStretch, + const float textureAspectRatio); + + Transform _polylineTransform; + QVector _lastPoints; + QVector _lastNormals; + QVector _lastStrokeColors; + QVector _lastStrokeWidths; + gpu::BufferPointer _verticesBuffer; + + uint32_t _numVertices { 0 }; + bool _empty{ true }; NetworkTexturePointer _texture; float _textureAspectRatio { 1.0f }; - bool _textureLoaded { false }; - bool _isUVModeStretch; - bool _faceCamera; - bool _glow; - - int _numVertices; - gpu::BufferPointer _polylineDataBuffer; - gpu::BufferPointer _polylineGeometryBuffer; - static gpu::PipelinePointer _pipeline; }; } } // namespace diff --git a/libraries/entities-renderer/src/paintStroke.slf b/libraries/entities-renderer/src/paintStroke.slf index 6ea088751f..f2c0d5572d 100644 --- a/libraries/entities-renderer/src/paintStroke.slf +++ b/libraries/entities-renderer/src/paintStroke.slf @@ -14,27 +14,21 @@ <@include DeferredBufferWrite.slh@> -<@include paintStroke.slh@> -<$declarePolyLineBuffers()$> +// the albedo texture +LAYOUT(binding=0) uniform sampler2D originalTexture; -LAYOUT(binding=0) uniform sampler2D _texture; - -layout(location=0) in vec3 _normalWS; -layout(location=1) in vec2 _texCoord; -layout(location=2) in vec4 _color; -layout(location=3) in float _distanceFromCenter; +// the interpolated normal +layout(location=0) in vec3 interpolatedNormal; +layout(location=1) in vec2 varTexcoord; +layout(location=2) in vec4 varColor; void main(void) { - vec4 texel = texture(_texture, _texCoord); - int frontCondition = 1 - 2 * int(gl_FrontFacing); - vec3 color = _color.rgb * texel.rgb; - float alpha = texel.a * _color.a; - - alpha *= mix(1.0, pow(1.0 - abs(_distanceFromCenter), 10.0), _polylineData.faceCameraGlow.y); - + vec4 texel = texture(originalTexture, varTexcoord); + int frontCondition = 1 -int(gl_FrontFacing) * 2; + vec3 color = varColor.rgb; packDeferredFragmentTranslucent( - float(frontCondition) * _normalWS, - alpha, - color, - DEFAULT_ROUGHNESS); + float(frontCondition) * interpolatedNormal, + texel.a * varColor.a, + color * texel.rgb, + 10.0); } diff --git a/libraries/entities-renderer/src/paintStroke.slh b/libraries/entities-renderer/src/paintStroke.slh deleted file mode 100644 index 6189ac461b..0000000000 --- a/libraries/entities-renderer/src/paintStroke.slh +++ /dev/null @@ -1,48 +0,0 @@ - - -<@if not PAINTSTROKE_SLH@> -<@def PAINTSTROKE_SLH@> - -<@include paintStroke_Shared.slh@> -<@include gpu/ShaderConstants.h@> - -<@func declarePolyLineBuffers() @> - -// Hack comment to absorb the extra '//' scribe prepends - -#if !defined(GPU_SSBO_TRANSFORM_OBJECT) -LAYOUT(binding=GPU_RESOURCE_BUFFER_SLOT0_TEXTURE) uniform samplerBuffer polylineVerticesBuffer; -PolylineVertex getPolylineVertex(int i) { - int offset = 4 * i; - PolylineVertex vertex; - vertex.positionAndUCoord = texelFetch(polylineVerticesBuffer, offset); - vertex.color = texelFetch(polylineVerticesBuffer, offset + 1); - vertex.normal = texelFetch(polylineVerticesBuffer, offset + 2); - vertex.binormalAndHalfWidth = texelFetch(polylineVerticesBuffer, offset + 3); - return vertex; -} -#else -LAYOUT_STD140(binding=GPU_RESOURCE_BUFFER_SLOT0_STORAGE) buffer polylineVerticesBuffer { - PolylineVertex _vertices[]; -}; -PolylineVertex getPolylineVertex(int i) { - PolylineVertex vertex = _vertices[i]; - return vertex; -} -#endif - -LAYOUT_STD140(binding=0) uniform polylineDataBuffer { - PolylineData _polylineData; -}; - -<@endfunc@> - -<@endif@> diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index c033d2c247..ecf52d61cf 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -17,45 +17,23 @@ <@include gpu/Transform.slh@> <$declareStandardTransform()$> -<@include paintStroke.slh@> -<$declarePolyLineBuffers()$> +// the interpolated normal +layout(location=0) out vec3 interpolatedNormal; -layout(location=0) out vec3 _normalWS; -layout(location=1) out vec2 _texCoord; -layout(location=2) out vec4 _color; -layout(location=3) out float _distanceFromCenter; +//the diffuse texture +layout(location=1) out vec2 varTexcoord; + +layout(location=2) out vec4 varColor; void main(void) { - PolylineVertex vertex = getPolylineVertex(gl_VertexID / 2); - float evenVertex = float(gl_VertexID % 2 == 0); + varTexcoord = inTexCoord0.st; - _texCoord = vec2(vertex.positionAndUCoord.w, mix(1.0, 0.0, evenVertex)); - _color = color_sRGBAToLinear(vertex.color); + // pass along the diffuse color + varColor = color_sRGBAToLinear(inColor); + // standard transform TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); - _distanceFromCenter = -1.0 + 2.0 * evenVertex; - vec4 position = vec4(vertex.positionAndUCoord.xyz, 1.0); - vec3 normal = vertex.normal.xyz; - vec3 binormal = vertex.binormalAndHalfWidth.xyz; - if (_polylineData.faceCameraGlow.x != 0.0) { - vec4 posEye; - vec3 normalEye; - vec3 binormalEye; - <$transformModelToEyePos(cam, obj, position, posEye)$> - <$transformModelToEyeDir(cam, obj, normal, normalEye)$> - <$transformModelToEyeDir(cam, obj, binormal, binormalEye)$> - - vec3 tangentEye = cross(binormalEye, normalEye); - // new normal faces the camera - normalEye = normalize(posEye.xyz); - binormalEye = normalize(cross(normalEye, tangentEye)); - posEye.xyz += _distanceFromCenter * vertex.binormalAndHalfWidth.w * binormalEye; - <$transformEyeToClipPos(cam, posEye, gl_Position)$> - <$transformEyeToWorldDir(cam, normalEye, _normalWS)$> - } else { - position.xyz += _distanceFromCenter * vertex.binormalAndHalfWidth.w * binormal; - <$transformModelToClipPos(cam, obj, position, gl_Position)$> - <$transformModelToWorldDir(cam, obj, normal, _normalWS)$> - } + <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> + <$transformModelToEyeDir(cam, obj, inNormal.xyz, interpolatedNormal)$> } \ No newline at end of file diff --git a/libraries/entities-renderer/src/paintStroke_Shared.slh b/libraries/entities-renderer/src/paintStroke_Shared.slh deleted file mode 100644 index 460f6c6042..0000000000 --- a/libraries/entities-renderer/src/paintStroke_Shared.slh +++ /dev/null @@ -1,25 +0,0 @@ -// glsl / C++ compatible source as interface for FadeEffect -#ifdef __cplusplus -# define _VEC4 glm::vec4 -# define _VEC2 glm::vec2 -#else -# define _VEC4 vec4 -# define _VEC2 vec2 -#endif - -struct PolylineVertex { - _VEC4 positionAndUCoord; - _VEC4 color; - _VEC4 normal; - _VEC4 binormalAndHalfWidth; -}; - -struct PolylineData { - _VEC2 faceCameraGlow; - _VEC2 spare; -}; - -// <@if 1@> -// Trigger Scribe include -// <@endif@> -// \ No newline at end of file diff --git a/libraries/entities-renderer/src/paintStroke_fade.slf b/libraries/entities-renderer/src/paintStroke_fade.slf new file mode 100644 index 0000000000..fa6d0aab75 --- /dev/null +++ b/libraries/entities-renderer/src/paintStroke_fade.slf @@ -0,0 +1,52 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// paintStroke_fade.frag +// fragment shader +// +// Created by Olivier Prat on 19/07/17. +// Copyright 2017 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 DeferredBufferWrite.slh@> + +<@include Fade.slh@> +<$declareFadeFragment()$> + +// the albedo texture +LAYOUT(binding=0) uniform sampler2D originalTexture; + +// the interpolated normal +layout(location=0) in vec3 interpolatedNormal; +layout(location=1) in vec2 varTexcoord; +layout(location=2) in vec4 varColor; +layout(location=3) in vec4 _worldPosition; + +struct PolyLineUniforms { + vec3 color; +}; + +LAYOUT(binding=0) uniform polyLineBuffer { + PolyLineUniforms polyline; +}; + +void main(void) { + vec3 fadeEmissive; + FadeObjectParams fadeParams; + + <$fetchFadeObjectParams(fadeParams)$> + applyFade(fadeParams, _worldPosition.xyz, fadeEmissive); + + vec4 texel = texture(originalTexture, varTexcoord); + int frontCondition = 1 -int(gl_FrontFacing) * 2; + vec3 color = varColor.rgb; + packDeferredFragmentTranslucent( + interpolatedNormal * float(frontCondition), + texel.a * varColor.a, + polyline.color * texel.rgb + fadeEmissive, + 10.0); +} diff --git a/libraries/entities-renderer/src/paintStroke_fade.slv b/libraries/entities-renderer/src/paintStroke_fade.slv new file mode 100644 index 0000000000..f6fcb18c98 --- /dev/null +++ b/libraries/entities-renderer/src/paintStroke_fade.slv @@ -0,0 +1,43 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// paintStroke_fade.vert +// vertex shader +// +// Created by Olivier Prat on 19/07/17. +// Copyright 2017 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 gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +// the interpolated normal +layout(location=0) out vec3 interpolatedNormal; + +//the diffuse texture +layout(location=1) out vec2 varTexcoord; + +layout(location=2) out vec4 varColor; +layout(location=3) out vec4 _worldPosition; + +void main(void) { + + varTexcoord = inTexCoord0.st; + + // pass along the diffuse color + varColor = color_sRGBAToLinear(inColor); + + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, inPosition, gl_Position)$> + <$transformModelToEyeDir(cam, obj, inNormal.xyz, interpolatedNormal)$> + <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> +} \ No newline at end of file diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 17dc0cdaba..86ad35b8f5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -527,8 +527,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_STROKE_NORMALS, normals); CHECK_PROPERTY_CHANGE(PROP_STROKE_COLORS, strokeColors); CHECK_PROPERTY_CHANGE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); - CHECK_PROPERTY_CHANGE(PROP_LINE_GLOW, glow); - CHECK_PROPERTY_CHANGE(PROP_LINE_FACE_CAMERA, faceCamera); // Shape CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); @@ -741,6 +739,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * @see {@link Entities.EntityProperties-Image|EntityProperties-Image} * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} * @see {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} * @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid} @@ -786,6 +785,33 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * }); */ +/**jsdoc + * The "Line" {@link Entities.EntityType|EntityType} draws thin, straight lines between a sequence of two or more + * points. Deprecated: Use PolyLines instead. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Line + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Must be sufficient to contain all the + * linePoints. + * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's + * position. A maximum of 70 points can be specified. The property's value is set only if all the linePoints + * lie within the entity's dimensions. + * @property {Color} color=255,255,255 - The color of the line. + * @example Draw lines in a "V". + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 }, + * { x: 1, y: 1, z: 0 }, + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + /**jsdoc * The "Material" {@link Entities.EntityType|EntityType} modifies the existing materials on * {@link Entities.EntityType|Model} entities, {@link Entities.EntityType|Shape} entities (albedo only), @@ -1025,8 +1051,6 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * format. * @property {boolean} isUVModeStretch=true - If true, the texture is stretched to fill the whole line, otherwise * the texture repeats along the line. - * @property {bool} glow=false - If true, the alpha of the strokes will drop off farther from the center. - * @property {bool} faceCamera=false - If true, each line segment will rotate to face the camera. * @example Draw a textured "V". * var entity = Entities.addEntity({ * type: "PolyLine", @@ -1593,6 +1617,13 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_Z_P_NEIGHBOR_ID, zPNeighborID); } + // Lines + if (_type == EntityTypes::Line) { + COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); + + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); + } + // Polylines if (_type == EntityTypes::PolyLine) { COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color); @@ -1603,8 +1634,6 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_NORMALS, normals); COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_STROKE_COLORS, strokeColors, qVectorVec3Color); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_GLOW, glow); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_FACE_CAMERA, faceCamera); } // Materials @@ -1927,8 +1956,6 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(normals, qVectorVec3, setNormals); COPY_PROPERTY_FROM_QSCRIPTVALUE(strokeColors, qVectorVec3, setStrokeColors); COPY_PROPERTY_FROM_QSCRIPTVALUE(isUVModeStretch, bool, setIsUVModeStretch); - COPY_PROPERTY_FROM_QSCRIPTVALUE(glow, bool, setGlow); - COPY_PROPERTY_FROM_QSCRIPTVALUE(faceCamera, bool, setFaceCamera); // Shape COPY_PROPERTY_FROM_QSCRIPTVALUE(shape, QString, setShape); @@ -2181,8 +2208,6 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(normals); COPY_PROPERTY_IF_CHANGED(strokeColors); COPY_PROPERTY_IF_CHANGED(isUVModeStretch); - COPY_PROPERTY_IF_CHANGED(glow); - COPY_PROPERTY_IF_CHANGED(faceCamera); // Shape COPY_PROPERTY_IF_CHANGED(shape); @@ -2498,8 +2523,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_STROKE_NORMALS, Normals, normals, QVector); ADD_PROPERTY_TO_MAP(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector); ADD_PROPERTY_TO_MAP(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, QVector); - ADD_PROPERTY_TO_MAP(PROP_LINE_GLOW, Glow, glow); - ADD_PROPERTY_TO_MAP(PROP_LINE_FACE_CAMERA, FaceCamera, faceCamera, bool); // Shape ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString); @@ -2849,6 +2872,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI()); } + if (properties.getType() == EntityTypes::Line) { + APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); + + APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, properties.getLinePoints()); + } + if (properties.getType() == EntityTypes::PolyLine) { APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures()); @@ -2858,8 +2887,6 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_STROKE_NORMALS, properties.getPackedNormals()); APPEND_ENTITY_PROPERTY(PROP_STROKE_COLORS, properties.getPackedStrokeColors()); APPEND_ENTITY_PROPERTY(PROP_IS_UV_MODE_STRETCH, properties.getIsUVModeStretch()); - APPEND_ENTITY_PROPERTY(PROP_LINE_GLOW, properties.getGlow()); - APPEND_ENTITY_PROPERTY(PROP_LINE_FACE_CAMERA, properties.getFaceCamera()); } // NOTE: Spheres and Boxes are just special cases of Shape, and they need to include their PROP_SHAPE @@ -3278,6 +3305,12 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI); } + if (properties.getType() == EntityTypes::Line) { + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); + + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_POINTS, QVector, setLinePoints); + } + if (properties.getType() == EntityTypes::PolyLine) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures); @@ -3287,8 +3320,6 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_NORMALS, QByteArray, setPackedNormals); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STROKE_COLORS, QByteArray, setPackedStrokeColors); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IS_UV_MODE_STRETCH, bool, setIsUVModeStretch); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_GLOW, bool, setGlow); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_FACE_CAMERA, bool, setFaceCamera); } // NOTE: Spheres and Boxes are just special cases of Shape, and they need to include their PROP_SHAPE @@ -3634,8 +3665,6 @@ void EntityItemProperties::markAllChanged() { _normalsChanged = true; _strokeColorsChanged = true; _isUVModeStretchChanged = true; - _glowChanged = true; - _faceCameraChanged = true; // Shape _shapeChanged = true; @@ -4234,12 +4263,6 @@ QList EntityItemProperties::listChangedProperties() { if (isUVModeStretchChanged()) { out += "isUVModeStretch"; } - if (glowChanged()) { - out += "glow"; - } - if (faceCameraChanged()) { - out += "faceCamera"; - } // Shape if (shapeChanged()) { diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 9857878edf..fc4848bd20 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -37,6 +37,7 @@ #include "EntityPropertyFlags.h" #include "EntityPsuedoPropertyFlags.h" #include "LightEntityItem.h" +#include "LineEntityItem.h" #include "ParticleEffectEntityItem.h" #include "PolyVoxEntityItem.h" #include "SimulationOwner.h" @@ -77,6 +78,7 @@ class EntityItemProperties { friend class ImageEntityItem; friend class WebEntityItem; friend class ParticleEffectEntityItem; + friend class LineEntityItem; friend class PolyLineEntityItem; friend class PolyVoxEntityItem; friend class GridEntityItem; @@ -307,8 +309,6 @@ public: DEFINE_PROPERTY(PROP_STROKE_NORMALS, Normals, normals, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true); - DEFINE_PROPERTY(PROP_LINE_GLOW, Glow, glow, bool, false); - DEFINE_PROPERTY(PROP_LINE_FACE_CAMERA, FaceCamera, faceCamera, bool, false); // Shape DEFINE_PROPERTY_REF(PROP_SHAPE, Shape, shape, QString, "Sphere"); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 812b788b06..e7cccc31df 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -290,8 +290,6 @@ enum EntityPropertyList { PROP_STROKE_NORMALS = PROP_DERIVED_2, PROP_STROKE_COLORS = PROP_DERIVED_3, PROP_IS_UV_MODE_STRETCH = PROP_DERIVED_4, - PROP_LINE_GLOW = PROP_DERIVED_5, - PROP_LINE_FACE_CAMERA = PROP_DERIVED_6, // Shape PROP_SHAPE = PROP_DERIVED_0, diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 02e8682264..33ec92b8de 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1519,6 +1519,46 @@ bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function actor) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + if (!_entityTree) { + return false; + } + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID; + } + + EntityTypes::EntityType entityType = entity->getType(); + + if (entityType != EntityTypes::Line) { + return false; + } + + auto now = usecTimestampNow(); + + auto lineEntity = std::static_pointer_cast(entity); + bool success; + _entityTree->withWriteLock([&] { + success = actor(*lineEntity); + entity->setLastEdited(now); + entity->setLastBroadcast(now); + }); + + EntityItemProperties properties; + _entityTree->withReadLock([&] { + properties = entity->getProperties(); + }); + + properties.setLinePointsDirty(); + properties.setLastEdited(now); + + queueEntityMessage(PacketType::EntityEdit, entityID, properties); + return success; +} + bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) { PROFILE_RANGE(script_entities, __FUNCTION__); return polyVoxWorker(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) { @@ -1561,6 +1601,49 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3 }); } +bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector& points) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID; + } + + EntityTypes::EntityType entityType = entity->getType(); + + if (entityType == EntityTypes::Line) { + return setPoints(entityID, [points](LineEntityItem& lineEntity) -> bool + { + return (LineEntityItem*)lineEntity.setLinePoints(points); + }); + } + + return false; +} + +bool EntityScriptingInterface::appendPoint(QUuid entityID, const glm::vec3& point) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::setPoints no entity with ID" << entityID; + // There is no entity + return false; + } + + EntityTypes::EntityType entityType = entity->getType(); + + if (entityType == EntityTypes::Line) { + return setPoints(entityID, [point](LineEntityItem& lineEntity) -> bool + { + return (LineEntityItem*)lineEntity.appendPoint(point); + }); + } + + return false; +} + + bool EntityScriptingInterface::actionWorker(const QUuid& entityID, std::function actor) { if (!_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 512e88c8e1..890c666010 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -26,6 +26,7 @@ #include #include "PolyVoxEntityItem.h" +#include "LineEntityItem.h" #include "PolyLineEntityItem.h" #include "EntityTree.h" @@ -860,12 +861,75 @@ public slots: // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); + /**jsdoc + * Set the linePoints property of a {@link Entities.EntityType|Line} entity. + * @function Entities.setAllPoints + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Line} entity. + * @param {Vec3[]} points - The array of points to set the entity's linePoints property to. + * @returns {boolean} true if the entity's property was updated, otherwise false. The property + * may fail to be updated if the entity does not exist, the entity is not a {@link Entities.EntityType|Line} entity, + * one of the points is outside the entity's dimensions, or the number of points is greater than the maximum allowed. + * @example Change the shape of a Line entity. + * // Draw a horizontal line between two points. + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 0, z: 0 }, + * { x:1, y: -0, z: 0 } + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * // Change the line to be a "V". + * Script.setTimeout(function () { + * Entities.setAllPoints(entity, [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 }, + * { x: 1, y: 1, z: 0 }, + * ]); + * }, 2000); + */ + Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); + + /**jsdoc + * Append a point to a {@link Entities.EntityType|Line} entity. + * @function Entities.appendPoint + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Line} entity. + * @param {Vec3} point - The point to add to the line. The coordinates are relative to the entity's position. + * @returns {boolean} true if the point was added to the line, otherwise false. The point may + * fail to be added if the entity does not exist, the entity is not a {@link Entities.EntityType|Line} entity, the + * point is outside the entity's dimensions, or the maximum number of points has been reached. + * @example Append a point to a Line entity. + * // Draw a line between two points. + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 } + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * // Add a third point to create a "V". + * Entities.appendPoint(entity, { x: 1, y: 1, z: 0 }); + */ + Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); + /**jsdoc * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about to the program log. * @function Entities.dumpTree */ Q_INVOKABLE void dumpTree() const; + /**jsdoc * Add an action to an entity. An action is registered with the physics engine and is applied every physics simulation * step. Any entity may have more than one action associated with it, but only as many as will fit in an entity's @@ -1914,6 +1978,7 @@ private slots: private: bool actionWorker(const QUuid& entityID, std::function actor); bool polyVoxWorker(QUuid entityID, std::function actor); + bool setPoints(QUuid entityID, std::function actor); void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties); bool addLocalEntityCopy(EntityItemProperties& propertiesWithSimID, EntityItemID& id, bool isClone = false); diff --git a/libraries/entities/src/EntityTypes.cpp b/libraries/entities/src/EntityTypes.cpp index 45a1e92073..e511af83b0 100644 --- a/libraries/entities/src/EntityTypes.cpp +++ b/libraries/entities/src/EntityTypes.cpp @@ -26,6 +26,7 @@ #include "TextEntityItem.h" #include "ImageEntityItem.h" #include "WebEntityItem.h" +#include "LineEntityItem.h" #include "PolyLineEntityItem.h" #include "PolyVoxEntityItem.h" #include "GridEntityItem.h" @@ -49,6 +50,7 @@ REGISTER_ENTITY_TYPE(Text) REGISTER_ENTITY_TYPE(Image) REGISTER_ENTITY_TYPE(Web) REGISTER_ENTITY_TYPE(ParticleEffect) +REGISTER_ENTITY_TYPE(Line) REGISTER_ENTITY_TYPE(PolyLine) REGISTER_ENTITY_TYPE(PolyVox) REGISTER_ENTITY_TYPE(Grid) diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index c9b9558265..29a695718e 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -70,6 +70,8 @@ public: * "ParticleEffect"A particle system that can be used to simulate things such as fire, * smoke, snow, magic spells, etc. * {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * "Line"A sequence of one or more simple straight lines. + * {@link Entities.EntityProperties-Line|EntityProperties-Line} * "PolyLine"A sequence of one or more textured straight lines. * {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} * "PolyVox"A set of textured voxels. @@ -97,6 +99,7 @@ public: Image, Web, ParticleEffect, + Line, PolyLine, PolyVox, Grid, diff --git a/libraries/entities/src/LineEntityItem.cpp b/libraries/entities/src/LineEntityItem.cpp new file mode 100644 index 0000000000..12d1178690 --- /dev/null +++ b/libraries/entities/src/LineEntityItem.cpp @@ -0,0 +1,175 @@ +// +// LineEntityItem.cpp +// libraries/entities/src +// +// Created by Seth Alves on 5/11/15. +// Copyright 2015 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 "LineEntityItem.h" + +#include + +#include + +#include "EntitiesLogging.h" +#include "EntityItemProperties.h" +#include "EntityTree.h" +#include "EntityTreeElement.h" +#include "OctreeConstants.h" + +const int LineEntityItem::MAX_POINTS_PER_LINE = 70; + + +EntityItemPointer LineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { + EntityItemPointer entity(new LineEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); + entity->setProperties(properties); + return entity; +} + +LineEntityItem::LineEntityItem(const EntityItemID& entityItemID) : + EntityItem(entityItemID) +{ + _type = EntityTypes::Line; +} + +EntityItemProperties LineEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + + EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class + + COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(linePoints, getLinePoints); + + return properties; +} + +bool LineEntityItem::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class + + SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(linePoints, setLinePoints); + + if (somethingChanged) { + bool wantDebug = false; + if (wantDebug) { + uint64_t now = usecTimestampNow(); + int elapsed = now - getLastEdited(); + qCDebug(entities) << "LineEntityItem::setProperties() AFTER update... edited AGO=" << elapsed << + "now=" << now << " getLastEdited()=" << getLastEdited(); + } + setLastEdited(properties._lastEdited); + } + return somethingChanged; +} + +bool LineEntityItem::appendPoint(const glm::vec3& point) { + if (_points.size() > MAX_POINTS_PER_LINE - 1) { + qCDebug(entities) << "MAX POINTS REACHED!"; + return false; + } + glm::vec3 halfBox = getScaledDimensions() * 0.5f; + if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { + qCDebug(entities) << "Point is outside entity's bounding box"; + return false; + } + withWriteLock([&] { + _points << point; + _pointsChanged = true; + }); + return true; +} + +bool LineEntityItem::setLinePoints(const QVector& points) { + if (points.size() > MAX_POINTS_PER_LINE) { + return false; + } + glm::vec3 halfBox = getScaledDimensions() * 0.5f; + for (int i = 0; i < points.size(); i++) { + glm::vec3 point = points.at(i); + if ( (point.x < - halfBox.x || point.x > halfBox.x) || (point.y < -halfBox.y || point.y > halfBox.y) || (point.z < - halfBox.z || point.z > halfBox.z) ) { + qCDebug(entities) << "Point is outside entity's bounding box"; + return false; + } + } + + withWriteLock([&] { + _points = points; + _pointsChanged = true; + }); + return true; +} + +int LineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor); + READ_ENTITY_PROPERTY(PROP_LINE_POINTS, QVector, setLinePoints); + + return bytesRead; +} + + +EntityPropertyFlags LineEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); + requestedProperties += PROP_COLOR; + requestedProperties += PROP_LINE_POINTS; + return requestedProperties; +} + +void LineEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); + APPEND_ENTITY_PROPERTY(PROP_LINE_POINTS, getLinePoints()); +} + +void LineEntityItem::debugDump() const { + quint64 now = usecTimestampNow(); + qCDebug(entities) << " LINE EntityItem id:" << getEntityItemID() << "---------------------------------------------"; + qCDebug(entities) << " color:" << _color; + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); +} + +glm::u8vec3 LineEntityItem::getColor() const { + return resultWithReadLock([&] { + return _color; + }); +} + +void LineEntityItem::setColor(const glm::u8vec3& value) { + withWriteLock([&] { + _color = value; + }); +} + +QVector LineEntityItem::getLinePoints() const { + QVector result; + withReadLock([&] { + result = _points; + }); + return result; +} + +void LineEntityItem::resetPointsChanged() { + withWriteLock([&] { + _pointsChanged = false; + }); +} diff --git a/libraries/entities/src/LineEntityItem.h b/libraries/entities/src/LineEntityItem.h new file mode 100644 index 0000000000..86ca6065bb --- /dev/null +++ b/libraries/entities/src/LineEntityItem.h @@ -0,0 +1,77 @@ +// +// LineEntityItem.h +// libraries/entities/src +// +// Created by Seth Alves on 5/11/15. +// Copyright 2015 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 +// + +#ifndef hifi_LineEntityItem_h +#define hifi_LineEntityItem_h + +#include "EntityItem.h" + +class LineEntityItem : public EntityItem { + public: + static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties); + + LineEntityItem(const EntityItemID& entityItemID); + + ALLOW_INSTANTIATION // This class can be instantiated + + // methods for getting/setting all properties of an entity + virtual EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override; + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + glm::u8vec3 getColor() const; + void setColor(const glm::u8vec3& value); + + bool setLinePoints(const QVector& points); + bool appendPoint(const glm::vec3& point); + + QVector getLinePoints() const; + + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } + + // never have a ray intersection pick a LineEntityItem. + virtual bool supportsDetailedIntersection() const override { return true; } + virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + OctreeElementPointer& element, float& distance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, + bool precisionPicking) const override { return false; } + virtual bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, + BoxFace& face, glm::vec3& surfaceNormal, + QVariantMap& extraInfo, + bool precisionPicking) const override { return false; } + bool pointsChanged() const { return _pointsChanged; } + void resetPointsChanged(); + virtual void debugDump() const override; + static const int MAX_POINTS_PER_LINE; + + private: + glm::u8vec3 _color; + QVector _points; + bool _pointsChanged { true }; +}; + +#endif // hifi_LineEntityItem_h diff --git a/libraries/entities/src/PolyLineEntityItem.cpp b/libraries/entities/src/PolyLineEntityItem.cpp index ee59b5b45a..88f2d14ae6 100644 --- a/libraries/entities/src/PolyLineEntityItem.cpp +++ b/libraries/entities/src/PolyLineEntityItem.cpp @@ -24,6 +24,7 @@ const float PolyLineEntityItem::DEFAULT_LINE_WIDTH = 0.1f; const int PolyLineEntityItem::MAX_POINTS_PER_LINE = 60; + EntityItemPointer PolyLineEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { EntityItemPointer entity(new PolyLineEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); @@ -36,6 +37,7 @@ PolyLineEntityItem::PolyLineEntityItem(const EntityItemID& entityItemID) : Entit } EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { + QWriteLocker lock(&_quadReadWriteLock); EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor); @@ -46,13 +48,11 @@ EntityItemProperties PolyLineEntityItem::getProperties(const EntityPropertyFlags COPY_ENTITY_PROPERTY_TO_PROPERTIES(normals, getNormals); COPY_ENTITY_PROPERTY_TO_PROPERTIES(strokeColors, getStrokeColors); COPY_ENTITY_PROPERTY_TO_PROPERTIES(isUVModeStretch, getIsUVModeStretch); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(glow, getGlow); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(faceCamera, getFaceCamera); - return properties; } bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { + QWriteLocker lock(&_quadReadWriteLock); bool somethingChanged = false; somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class @@ -64,8 +64,6 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(normals, setNormals); SET_ENTITY_PROPERTY_FROM_PROPERTIES(strokeColors, setStrokeColors); SET_ENTITY_PROPERTY_FROM_PROPERTIES(isUVModeStretch, setIsUVModeStretch); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(glow, setGlow); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(faceCamera, setFaceCamera); if (somethingChanged) { bool wantDebug = false; @@ -80,73 +78,125 @@ bool PolyLineEntityItem::setProperties(const EntityItemProperties& properties) { return somethingChanged; } -void PolyLineEntityItem::setLinePoints(const QVector& points) { - withWriteLock([&] { - _points = points; - _pointsChanged = true; - }); - computeAndUpdateDimensionsAndPosition(); + +bool PolyLineEntityItem::appendPoint(const glm::vec3& point) { + if (_points.size() > MAX_POINTS_PER_LINE - 1) { + qCDebug(entities) << "MAX POINTS REACHED!"; + return false; + } + + _points << point; + _pointsChanged = true; + + calculateScaleAndRegistrationPoint(); + + return true; } -void PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { + +bool PolyLineEntityItem::setStrokeWidths(const QVector& strokeWidths) { withWriteLock([&] { - _widths = strokeWidths; - _widthsChanged = true; + _strokeWidths = strokeWidths; + _strokeWidthsChanged = true; }); - computeAndUpdateDimensionsAndPosition(); + return true; } -void PolyLineEntityItem::setNormals(const QVector& normals) { +bool PolyLineEntityItem::setNormals(const QVector& normals) { withWriteLock([&] { _normals = normals; _normalsChanged = true; }); + return true; } -void PolyLineEntityItem::setStrokeColors(const QVector& strokeColors) { +bool PolyLineEntityItem::setStrokeColors(const QVector& strokeColors) { withWriteLock([&] { - _colors = strokeColors; - _colorsChanged = true; + _strokeColors = strokeColors; + _strokeColorsChanged = true; }); + return true; } -void PolyLineEntityItem::computeAndUpdateDimensionsAndPosition() { - QVector points; - QVector widths; - withReadLock([&] { - points = _points; - widths = _widths; +bool PolyLineEntityItem::setLinePoints(const QVector& points) { + if (points.size() > MAX_POINTS_PER_LINE) { + return false; + } + bool result = false; + withWriteLock([&] { + //Check to see if points actually changed. If they haven't, return before doing anything else + if (points.size() != _points.size()) { + _pointsChanged = true; + } else if (points.size() == _points.size()) { + //same number of points, so now compare every point + for (int i = 0; i < points.size(); i++) { + if (points.at(i) != _points.at(i)) { + _pointsChanged = true; + break; + } + } + } + if (!_pointsChanged) { + return; + } + + _points = points; + + result = true; }); - AABox container; - float maxWidth = 0.0f; - if (_points.length() > 0) { - container = AABox(_points[0] - 0.5f * ENTITY_ITEM_DEFAULT_DIMENSIONS, ENTITY_ITEM_DEFAULT_WIDTH); - for (int i = 0; i < points.length(); i++) { - container += points[i]; - maxWidth = glm::max(maxWidth, i < widths.length() ? widths[i] : DEFAULT_LINE_WIDTH); - } - } else { - container = AABox(-0.5f * ENTITY_ITEM_DEFAULT_DIMENSIONS, ENTITY_ITEM_DEFAULT_WIDTH); + if (result) { + calculateScaleAndRegistrationPoint(); } - // Adjust the box to account for the line width, assuming the worst case - container.setScaleStayCentered(container.getScale() + 2.0f * maxWidth); + return result; +} - bool success; - glm::vec3 center = container.calcCenter(); - setWorldPosition(center + getCenterPosition(success)); - setScaledDimensions(container.getScale()); - withWriteLock([&] { - _polylineCenter = center; +void PolyLineEntityItem::calculateScaleAndRegistrationPoint() { + glm::vec3 high(0.0f, 0.0f, 0.0f); + glm::vec3 low(0.0f, 0.0f, 0.0f); + int pointCount = 0; + glm::vec3 firstPoint; + withReadLock([&] { + pointCount = _points.size(); + if (pointCount > 0) { + firstPoint = _points.at(0); + } + for (int i = 0; i < pointCount; i++) { + const glm::vec3& point = _points.at(i); + high = glm::max(point, high); + low = glm::min(point, low); + } }); + + float magnitudeSquared = glm::length2(low - high); + vec3 newScale { 1 }; + vec3 newRegistrationPoint { 0.5f }; + + const float EPSILON = 0.0001f; + const float EPSILON_SQUARED = EPSILON * EPSILON; + const float HALF_LINE_WIDTH = 0.075f; // sadly _strokeWidths() don't seem to correspond to reality, so just use a flat assumption of the stroke width + const vec3 QUARTER_LINE_WIDTH { HALF_LINE_WIDTH * 0.5f }; + if (pointCount > 1 && magnitudeSquared > EPSILON_SQUARED) { + newScale = glm::abs(high) + glm::abs(low) + vec3(HALF_LINE_WIDTH); + // Center the poly line in the bounding box + glm::vec3 startPointInScaleSpace = firstPoint - low; + startPointInScaleSpace += QUARTER_LINE_WIDTH; + newRegistrationPoint = startPointInScaleSpace / newScale; + } + + // if Polyline has only one or fewer points, use default dimension settings + setScaledDimensions(newScale); + EntityItem::setRegistrationPoint(newRegistrationPoint); } int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { + + QWriteLocker lock(&_quadReadWriteLock); int bytesRead = 0; const unsigned char* dataAt = data; @@ -158,8 +208,6 @@ int PolyLineEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* da READ_ENTITY_PROPERTY(PROP_STROKE_NORMALS, QVector, setNormals); READ_ENTITY_PROPERTY(PROP_STROKE_COLORS, QVector, setStrokeColors); READ_ENTITY_PROPERTY(PROP_IS_UV_MODE_STRETCH, bool, setIsUVModeStretch); - READ_ENTITY_PROPERTY(PROP_LINE_GLOW, bool, setGlow); - READ_ENTITY_PROPERTY(PROP_LINE_FACE_CAMERA, bool, setFaceCamera); return bytesRead; } @@ -174,8 +222,6 @@ EntityPropertyFlags PolyLineEntityItem::getEntityProperties(EncodeBitstreamParam requestedProperties += PROP_STROKE_NORMALS; requestedProperties += PROP_STROKE_COLORS; requestedProperties += PROP_IS_UV_MODE_STRETCH; - requestedProperties += PROP_LINE_GLOW; - requestedProperties += PROP_LINE_FACE_CAMERA; return requestedProperties; } @@ -187,6 +233,7 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode int& propertyCount, OctreeElement::AppendState& appendState) const { + QWriteLocker lock(&_quadReadWriteLock); bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor()); @@ -197,8 +244,6 @@ void PolyLineEntityItem::appendSubclassData(OctreePacketData* packetData, Encode APPEND_ENTITY_PROPERTY(PROP_STROKE_NORMALS, getNormals()); APPEND_ENTITY_PROPERTY(PROP_STROKE_COLORS, getStrokeColors()); APPEND_ENTITY_PROPERTY(PROP_IS_UV_MODE_STRETCH, getIsUVModeStretch()); - APPEND_ENTITY_PROPERTY(PROP_LINE_GLOW, getGlow()); - APPEND_ENTITY_PROPERTY(PROP_LINE_FACE_CAMERA, getFaceCamera()); } void PolyLineEntityItem::debugDump() const { @@ -210,49 +255,61 @@ void PolyLineEntityItem::debugDump() const { qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); } + + QVector PolyLineEntityItem::getLinePoints() const { - return resultWithReadLock>([&] { - return _points; + QVector result; + withReadLock([&] { + result = _points; }); + return result; } QVector PolyLineEntityItem::getNormals() const { - return resultWithReadLock>([&] { - return _normals; + QVector result; + withReadLock([&] { + result = _normals; }); + return result; } QVector PolyLineEntityItem::getStrokeColors() const { - return resultWithReadLock>([&] { - return _colors; + QVector result; + withReadLock([&] { + result = _strokeColors; }); + return result; } QVector PolyLineEntityItem::getStrokeWidths() const { - return resultWithReadLock>([&] { - return _widths; + QVector result; + withReadLock([&] { + result = _strokeWidths; }); + return result; } QString PolyLineEntityItem::getTextures() const { - return resultWithReadLock([&] { - return _textures; + QString result; + withReadLock([&] { + result = _textures; }); + return result; } void PolyLineEntityItem::setTextures(const QString& textures) { withWriteLock([&] { if (_textures != textures) { _textures = textures; - _texturesChanged = true; + _texturesChangedFlag = true; } }); } void PolyLineEntityItem::setColor(const glm::u8vec3& value) { withWriteLock([&] { + _strokeColorsChanged = true; _color = value; - _colorsChanged = true; }); } diff --git a/libraries/entities/src/PolyLineEntityItem.h b/libraries/entities/src/PolyLineEntityItem.h index 69afaf175c..4420a123c5 100644 --- a/libraries/entities/src/PolyLineEntityItem.h +++ b/libraries/entities/src/PolyLineEntityItem.h @@ -44,42 +44,35 @@ class PolyLineEntityItem : public EntityItem { glm::u8vec3 getColor() const; void setColor(const glm::u8vec3& value); - static const int MAX_POINTS_PER_LINE; - void setLinePoints(const QVector& points); + bool setLinePoints(const QVector& points); + bool appendPoint(const glm::vec3& point); QVector getLinePoints() const; - glm::vec3 getPolylineCenter() const { return _polylineCenter; } - - static const float DEFAULT_LINE_WIDTH; - void setStrokeWidths(const QVector& strokeWidths); - QVector getStrokeWidths() const; - - void setNormals(const QVector& normals); + bool setNormals(const QVector& normals); QVector getNormals() const; - void setStrokeColors(const QVector& strokeColors); + bool setStrokeColors(const QVector& strokeColors); QVector getStrokeColors() const; + bool setStrokeWidths(const QVector& strokeWidths); + QVector getStrokeWidths() const; + void setIsUVModeStretch(bool isUVModeStretch){ _isUVModeStretch = isUVModeStretch; } bool getIsUVModeStretch() const{ return _isUVModeStretch; } QString getTextures() const; void setTextures(const QString& textures); - void setGlow(bool glow) { _glow = glow; } - bool getGlow() const { return _glow; } - - void setFaceCamera(bool faceCamera) { _faceCamera = faceCamera; } - bool getFaceCamera() const { return _faceCamera; } + virtual ShapeType getShapeType() const override { return SHAPE_TYPE_NONE; } bool pointsChanged() const { return _pointsChanged; } bool normalsChanged() const { return _normalsChanged; } - bool colorsChanged() const { return _colorsChanged; } - bool widthsChanged() const { return _widthsChanged; } - bool texturesChanged() const { return _texturesChanged; } + bool strokeColorsChanged() const { return _strokeColorsChanged; } + bool strokeWidthsChanged() const { return _strokeWidthsChanged; } + bool texturesChanged() const { return _texturesChangedFlag; } + void resetTexturesChanged() { _texturesChangedFlag = false; } + void resetPolyLineChanged() { _strokeColorsChanged = _strokeWidthsChanged = _normalsChanged = _pointsChanged = false; } - void resetTexturesChanged() { _texturesChanged = false; } - void resetPolyLineChanged() { _colorsChanged = _widthsChanged = _normalsChanged = _pointsChanged = false; } // never have a ray intersection pick a PolyLineEntityItem. virtual bool supportsDetailedIntersection() const override { return true; } @@ -92,27 +85,29 @@ class PolyLineEntityItem : public EntityItem { BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, bool precisionPicking) const override { return false; } + // disable these external interfaces as PolyLineEntities caculate their own dimensions based on the points they contain + virtual void setRegistrationPoint(const glm::vec3& value) override {}; // FIXME: this is suspicious! + virtual void debugDump() const override; + static const float DEFAULT_LINE_WIDTH; + static const int MAX_POINTS_PER_LINE; private: - void computeAndUpdateDimensionsAndPosition(); + void calculateScaleAndRegistrationPoint(); protected: glm::u8vec3 _color; + bool _pointsChanged { true }; + bool _normalsChanged { true }; + bool _strokeColorsChanged { true }; + bool _strokeWidthsChanged { true }; QVector _points; - glm::vec3 _polylineCenter; QVector _normals; - QVector _colors; - QVector _widths; + QVector _strokeColors; + QVector _strokeWidths; QString _textures; bool _isUVModeStretch; - bool _glow; - bool _faceCamera; - - bool _pointsChanged { false }; - bool _normalsChanged { false }; - bool _colorsChanged { false }; - bool _widthsChanged { false }; - bool _texturesChanged { false }; + bool _texturesChangedFlag { false }; + mutable QReadWriteLock _quadReadWriteLock; }; #endif // hifi_PolyLineEntityItem_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index ebd747fd39..c8e2aaf51e 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::MissingLineProperties); + return static_cast(EntityVersion::MorePropertiesCleanup); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 1ed2dd83ff..33f3f967c9 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -251,7 +251,7 @@ enum class EntityVersion : PacketVersion { ImageEntities, GridEntities, MissingTextProperties, - MissingLineProperties + MorePropertiesCleanup }; enum class EntityScriptCallMethodVersion : PacketVersion {