diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index 8185a24078..d50a9c545c 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -10,7 +10,7 @@ Script.include("cookies.js"); -var panel = new Panel(10, 800); +var panel = new Panel(10, 100); panel.newSlider("Num Feed Opaques", 0, 1000, function(value) { }, @@ -66,6 +66,12 @@ panel.newSlider("Max Drawn Overlay3Ds", -1, 100, function(value) { return (value); } ); +panel.newCheckbox("Display status", + function(value) { Scene.setEngineDisplayItemStatus(value); }, + function() { return Scene.doEngineDisplayItemStatus(); }, + function(value) { return (value); } +); + var tickTackPeriod = 500; function updateCounters() { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2f1797e649..c1397beb28 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3548,6 +3548,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se renderContext._maxDrawnTransparentItems = sceneInterface->getEngineMaxDrawnTransparentItems(); renderContext._maxDrawnOverlay3DItems = sceneInterface->getEngineMaxDrawnOverlay3DItems(); + renderContext._drawItemStatus = sceneInterface->doEngineDisplayItemStatus(); + renderArgs->_shouldRender = LODManager::shouldRender; renderContext.args = renderArgs; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 14a64d289e..ae1c97f07f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -167,6 +167,25 @@ namespace render { } } +void makeEntityItemStatusGetters(RenderableModelEntityItem* entity, render::Item::Status::Getters& statusGetters) { + statusGetters.push_back([entity] () -> render::Item::Status::Value { + quint64 delta = usecTimestampNow() - entity->getLastEditedFromRemote(); + const float WAIT_THRESHOLD_INV = 1.0f / (0.2f * USECS_PER_SECOND); + float normalizedDelta = delta * WAIT_THRESHOLD_INV; + // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD + // Color is red if last update is after WAIT_THRESHOLD, green otherwise (120 deg is green) + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::GREEN : render::Item::Status::Value::RED)); + }); + statusGetters.push_back([entity] () -> render::Item::Status::Value { + quint64 delta = usecTimestampNow() - entity->getLastBroadcast(); + const float WAIT_THRESHOLD_INV = 1.0f / (0.4f * USECS_PER_SECOND); + float normalizedDelta = delta * WAIT_THRESHOLD_INV; + // Status icon will scale from 1.0f down to 0.0f after WAIT_THRESHOLD + // Color is Magenta if last update is after WAIT_THRESHOLD, cyan otherwise (180 deg is green) + return render::Item::Status::Value(1.0f - normalizedDelta, (normalizedDelta > 1.0f ? render::Item::Status::Value::MAGENTA : render::Item::Status::Value::CYAN)); + }); +} + bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_ptr scene, render::PendingChanges& pendingChanges) { _myMetaItem = scene->allocateID(); @@ -177,7 +196,9 @@ bool RenderableModelEntityItem::addToScene(EntityItemPointer self, std::shared_p pendingChanges.resetItem(_myMetaItem, renderPayload); if (_model) { - return _model->addToScene(scene, pendingChanges); + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + return _model->addToScene(scene, pendingChanges, statusGetters); } return true; @@ -214,7 +235,10 @@ void RenderableModelEntityItem::render(RenderArgs* args) { render::PendingChanges pendingChanges; if (_model->needsFixupInScene()) { _model->removeFromScene(scene, pendingChanges); - _model->addToScene(scene, pendingChanges); + + render::Item::Status::Getters statusGetters; + makeEntityItemStatusGetters(this, statusGetters); + _model->addToScene(scene, pendingChanges, statusGetters); } scene->enqueuePendingChanges(pendingChanges); diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index bff99e7ec3..ee028e79e6 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -159,6 +159,12 @@ void Batch::setProjectionTransform(const Mat4& proj) { _params.push_back(cacheData(sizeof(Mat4), &proj)); } +void Batch::setViewportTransform(const Vec4i& viewport) { + ADD_COMMAND(setViewportTransform); + + _params.push_back(cacheData(sizeof(Vec4i), &viewport)); +} + void Batch::setPipeline(const PipelinePointer& pipeline) { ADD_COMMAND(setPipeline); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 9c97db65ef..835e872b4a 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -105,6 +105,7 @@ public: void setModelTransform(const Transform& model); void setViewTransform(const Transform& view); void setProjectionTransform(const Mat4& proj); + void setViewportTransform(const Vec4i& viewport); // Viewport is xy = low left corner in the framebuffer, zw = width height of the viewport // Pipeline Stage void setPipeline(const PipelinePointer& pipeline); @@ -154,6 +155,7 @@ public: void _glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); void _glUniform3fv(GLint location, GLsizei count, const GLfloat* value); void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value); + void _glUniform4iv(GLint location, GLsizei count, const GLint* value); void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value); void _glEnableVertexAttribArray(GLint location); @@ -177,6 +179,7 @@ public: COMMAND_setModelTransform, COMMAND_setViewTransform, COMMAND_setProjectionTransform, + COMMAND_setViewportTransform, COMMAND_setPipeline, COMMAND_setStateBlendFactor, @@ -217,6 +220,7 @@ public: COMMAND_glUniform3f, COMMAND_glUniform3fv, COMMAND_glUniform4fv, + COMMAND_glUniform4iv, COMMAND_glUniformMatrix4fv, COMMAND_glEnableVertexAttribArray, diff --git a/libraries/gpu/src/gpu/Config.slh b/libraries/gpu/src/gpu/Config.slh index 28f447a696..76be161822 100644 --- a/libraries/gpu/src/gpu/Config.slh +++ b/libraries/gpu/src/gpu/Config.slh @@ -18,11 +18,13 @@ <@elif GLPROFILE == MAC_GL @> <@def GPU_FEATURE_PROFILE GPU_LEGACY@> <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120@> + <@def VERSION_HEADER #version 120 +#extension GL_EXT_gpu_shader4 : enable@> <@else@> <@def GPU_FEATURE_PROFILE GPU_LEGACY@> <@def GPU_TRANSFORM_PROFILE GPU_LEGACY@> - <@def VERSION_HEADER #version 120@> + <@def VERSION_HEADER #version 120 +#extension GL_EXT_gpu_shader4 : enable@> <@endif@> <@endif@> diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 7cf913430d..ac71cc7940 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -38,6 +38,7 @@ typedef uint32 Offset; typedef glm::mat4 Mat4; typedef glm::mat3 Mat3; typedef glm::vec4 Vec4; +typedef glm::ivec4 Vec4i; typedef glm::vec3 Vec3; typedef glm::vec2 Vec2; typedef glm::ivec2 Vec2i; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 9004c4a8fe..302dc0e8be 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -29,6 +29,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setModelTransform), (&::gpu::GLBackend::do_setViewTransform), (&::gpu::GLBackend::do_setProjectionTransform), + (&::gpu::GLBackend::do_setViewportTransform), (&::gpu::GLBackend::do_setPipeline), (&::gpu::GLBackend::do_setStateBlendFactor), @@ -67,6 +68,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_glUniform3f), (&::gpu::GLBackend::do_glUniform3fv), (&::gpu::GLBackend::do_glUniform4fv), + (&::gpu::GLBackend::do_glUniform4iv), (&::gpu::GLBackend::do_glUniformMatrix4fv), (&::gpu::GLBackend::do_glEnableVertexAttribArray), @@ -190,6 +192,18 @@ void GLBackend::do_drawIndexed(Batch& batch, uint32 paramOffset) { } void GLBackend::do_drawInstanced(Batch& batch, uint32 paramOffset) { + updateInput(); + updateTransform(); + updatePipeline(); + + GLint numInstances = batch._params[paramOffset + 4]._uint; + Primitive primitiveType = (Primitive)batch._params[paramOffset + 3]._uint; + GLenum mode = _primitiveToGLmode[primitiveType]; + uint32 numVertices = batch._params[paramOffset + 2]._uint; + uint32 startVertex = batch._params[paramOffset + 1]._uint; + uint32 startInstance = batch._params[paramOffset + 0]._uint; + + glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); (void) CHECK_GL_ERROR(); } @@ -592,6 +606,31 @@ void GLBackend::do_glUniform4fv(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } +void Batch::_glUniform4iv(GLint location, GLsizei count, const GLint* value) { + ADD_COMMAND_GL(glUniform4iv); + + const int VEC4_SIZE = 4 * sizeof(int); + _params.push_back(cacheData(count * VEC4_SIZE, value)); + _params.push_back(count); + _params.push_back(location); + + DO_IT_NOW(_glUniform4iv, 3); +} +void GLBackend::do_glUniform4iv(Batch& batch, uint32 paramOffset) { + if (_pipeline._program == 0) { + // We should call updatePipeline() to bind the program but we are not doing that + // because these uniform setters are deprecated and we don;t want to create side effect + return; + } + updatePipeline(); + glUniform4iv( + batch._params[paramOffset + 2]._int, + batch._params[paramOffset + 1]._uint, + (const GLint*)batch.editData(batch._params[paramOffset + 0]._uint)); + + (void) CHECK_GL_ERROR(); +} + void Batch::_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { ADD_COMMAND_GL(glUniformMatrix4fv); diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 28236c68c9..d798e9aaac 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -96,7 +96,9 @@ public: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + GLuint _transformObject_model = -1; GLuint _transformCamera_viewInverse = -1; + GLuint _transformCamera_viewport = -1; #endif GLShader(); @@ -267,7 +269,8 @@ protected: void do_setModelTransform(Batch& batch, uint32 paramOffset); void do_setViewTransform(Batch& batch, uint32 paramOffset); void do_setProjectionTransform(Batch& batch, uint32 paramOffset); - + void do_setViewportTransform(Batch& batch, uint32 paramOffset); + void initTransform(); void killTransform(); // Synchronize the state cache of this Backend with the actual real state of the GL Context @@ -281,9 +284,11 @@ protected: Transform _model; Transform _view; Mat4 _projection; + Vec4i _viewport; bool _invalidModel; bool _invalidView; bool _invalidProj; + bool _invalidViewport; GLenum _lastMode; @@ -296,6 +301,7 @@ protected: _invalidModel(true), _invalidView(true), _invalidProj(false), + _invalidViewport(false), _lastMode(GL_TEXTURE) {} } _transform; @@ -329,7 +335,9 @@ protected: #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + GLint _program_transformObject_model = -1; GLint _program_transformCamera_viewInverse = -1; + GLint _program_transformCamera_viewport = -1; #endif State::Data _stateCache; @@ -390,6 +398,7 @@ protected: void do_glUniform3f(Batch& batch, uint32 paramOffset); void do_glUniform3fv(Batch& batch, uint32 paramOffset); void do_glUniform4fv(Batch& batch, uint32 paramOffset); + void do_glUniform4iv(Batch& batch, uint32 paramOffset); void do_glUniformMatrix4fv(Batch& batch, uint32 paramOffset); void do_glEnableVertexAttribArray(Batch& batch, uint32 paramOffset); diff --git a/libraries/gpu/src/gpu/GLBackendPipeline.cpp b/libraries/gpu/src/gpu/GLBackendPipeline.cpp index 06d9eadd87..a770cf89b3 100755 --- a/libraries/gpu/src/gpu/GLBackendPipeline.cpp +++ b/libraries/gpu/src/gpu/GLBackendPipeline.cpp @@ -73,7 +73,9 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + _pipeline._program_transformObject_model = -1; _pipeline._program_transformCamera_viewInverse = -1; + _pipeline._program_transformCamera_viewport = -1; #endif _pipeline._state = nullptr; @@ -91,7 +93,9 @@ void GLBackend::do_setPipeline(Batch& batch, uint32 paramOffset) { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + _pipeline._program_transformObject_model = pipelineObject->_program->_transformObject_model; _pipeline._program_transformCamera_viewInverse = pipelineObject->_program->_transformCamera_viewInverse; + _pipeline._program_transformCamera_viewport = pipelineObject->_program->_transformCamera_viewport; #endif } @@ -143,10 +147,20 @@ void GLBackend::updatePipeline() { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) #else + // If shader program needs the model we need to provide it + if (_pipeline._program_transformObject_model >= 0) { + glUniformMatrix4fv(_pipeline._program_transformObject_model, 1, false, (const GLfloat*) &_transform._transformObject._model); + } + // If shader program needs the inverseView we need to provide it if (_pipeline._program_transformCamera_viewInverse >= 0) { glUniformMatrix4fv(_pipeline._program_transformCamera_viewInverse, 1, false, (const GLfloat*) &_transform._transformCamera._viewInverse); } + + // If shader program needs the viewport we need to provide it + if (_pipeline._program_transformCamera_viewport >= 0) { + glUniform4fv(_pipeline._program_transformCamera_viewport, 1, (const GLfloat*) &_transform._transformCamera._viewport); + } #endif } diff --git a/libraries/gpu/src/gpu/GLBackendShader.cpp b/libraries/gpu/src/gpu/GLBackendShader.cpp index 45adbcdb3c..ec02c1333b 100755 --- a/libraries/gpu/src/gpu/GLBackendShader.cpp +++ b/libraries/gpu/src/gpu/GLBackendShader.cpp @@ -111,10 +111,20 @@ void makeBindings(GLBackend::GLShader* shader) { shader->_transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; } #else + loc = glGetUniformLocation(glprogram, "transformObject_model"); + if (loc >= 0) { + shader->_transformObject_model = loc; + } + loc = glGetUniformLocation(glprogram, "transformCamera_viewInverse"); if (loc >= 0) { shader->_transformCamera_viewInverse = loc; } + + loc = glGetUniformLocation(glprogram, "transformCamera_viewport"); + if (loc >= 0) { + shader->_transformCamera_viewport = loc; + } #endif } diff --git a/libraries/gpu/src/gpu/GLBackendTransform.cpp b/libraries/gpu/src/gpu/GLBackendTransform.cpp index 2e3c2dca70..21a2d57271 100755 --- a/libraries/gpu/src/gpu/GLBackendTransform.cpp +++ b/libraries/gpu/src/gpu/GLBackendTransform.cpp @@ -31,6 +31,12 @@ void GLBackend::do_setProjectionTransform(Batch& batch, uint32 paramOffset) { _transform._invalidProj = true; } +void GLBackend::do_setViewportTransform(Batch& batch, uint32 paramOffset) { + memcpy(&_transform._viewport, batch.editData(batch._params[paramOffset]._uint), sizeof(Vec4i)); + _transform._invalidViewport = true; +} + + void GLBackend::initTransform() { #if (GPU_TRANSFORM_PROFILE == GPU_CORE) glGenBuffers(1, &_transform._transformObjectBuffer); @@ -57,10 +63,13 @@ void GLBackend::killTransform() { } void GLBackend::syncTransformStateCache() { + _transform._invalidViewport = true; _transform._invalidProj = true; _transform._invalidView = true; _transform._invalidModel = true; + glGetIntegerv(GL_VIEWPORT, (GLint*) &_transform._viewport); + GLint currentMode; glGetIntegerv(GL_MATRIX_MODE, ¤tMode); _transform._lastMode = currentMode; @@ -78,6 +87,13 @@ void GLBackend::updateTransform() { GLint originalMatrixMode; glGetIntegerv(GL_MATRIX_MODE, &originalMatrixMode); // Check all the dirty flags and update the state accordingly + if (_transform._invalidViewport) { + _transform._transformCamera._viewport = glm::vec4(_transform._viewport); + + // Where we assign the GL viewport + glViewport(_transform._viewport.x, _transform._viewport.y, _transform._viewport.z, _transform._viewport.w); + } + if (_transform._invalidProj) { _transform._transformCamera._projection = _transform._projection; _transform._transformCamera._projectionInverse = glm::inverse(_transform._projection); @@ -100,7 +116,7 @@ void GLBackend::updateTransform() { } #if (GPU_TRANSFORM_PROFILE == GPU_CORE) - if (_transform._invalidView || _transform._invalidProj) { + if (_transform._invalidView || _transform._invalidProj || _transform._invalidViewport) { glBindBufferBase(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, 0); glBindBuffer(GL_ARRAY_BUFFER, _transform._transformCameraBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(_transform._transformCamera), (const void*) &_transform._transformCamera, GL_DYNAMIC_DRAW); @@ -162,7 +178,8 @@ void GLBackend::updateTransform() { #endif // Flags are clean - _transform._invalidView = _transform._invalidProj = _transform._invalidModel = false; + _transform._invalidView = _transform._invalidProj = _transform._invalidModel = _transform._invalidViewport = false; + glMatrixMode(originalMatrixMode); } diff --git a/libraries/gpu/src/gpu/Transform.slh b/libraries/gpu/src/gpu/Transform.slh index d01fe128ae..274032a642 100644 --- a/libraries/gpu/src/gpu/Transform.slh +++ b/libraries/gpu/src/gpu/Transform.slh @@ -87,10 +87,18 @@ TransformCamera getTransformCamera() { } uniform mat4 transformCamera_viewInverse; +uniform vec4 transformCamera_viewport; <@endif@> <@endfunc@> +<@func transformCameraViewport(cameraTransform, viewport)@> +<@if GPU_TRANSFORM_PROFILE == GPU_CORE@> + <$viewport$> = <$cameraTransform$>._viewport; +<@else@> + <$viewport$> = transformCamera_viewport; +<@endif@> +<@endfunc@> <@func transformModelToClipPos(cameraTransform, objectTransform, modelPos, clipPos)@> <@if GPU_TRANSFORM_PROFILE == GPU_CORE@> diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 93f3f345f0..1b94c70e57 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -910,6 +910,38 @@ bool Model::addToScene(std::shared_ptr scene, render::PendingChan return somethingAdded; } +bool Model::addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters) { + if (!_meshGroupsKnown && isLoadedWithTextures()) { + segregateMeshGroups(); + } + + bool somethingAdded = false; + + foreach (auto renderItem, _transparentRenderItems) { + auto item = scene->allocateID(); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(item, renderPayload); + _renderItems.insert(item, renderPayload); + somethingAdded = true; + } + + foreach (auto renderItem, _opaqueRenderItems) { + auto item = scene->allocateID(); + auto renderData = MeshPartPayload::Pointer(renderItem); + auto renderPayload = render::PayloadPointer(new MeshPartPayload::Payload(renderData)); + renderPayload->addStatusGetters(statusGetters); + pendingChanges.resetItem(item, renderPayload); + _renderItems.insert(item, renderPayload); + somethingAdded = true; + } + + _readyWhenAdded = readyToAddToScene(); + + return somethingAdded; +} + void Model::removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges) { foreach (auto item, _renderItems.keys()) { pendingChanges.removeItem(item); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3748403b97..a6ce566a36 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -118,6 +118,7 @@ public: bool needsFixupInScene() { return !_readyWhenAdded && readyToAddToScene(); } bool readyToAddToScene(RenderArgs* renderArgs = nullptr) { return !_needsReload && isRenderable() && isActive() && isLoadedWithTextures(); } bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); + bool addToScene(std::shared_ptr scene, render::PendingChanges& pendingChanges, render::Item::Status::Getters& statusGetters); void removeFromScene(std::shared_ptr scene, render::PendingChanges& pendingChanges); /// Sets the URL of the model to render. diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 850baf3d06..12a6d32ae5 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -17,6 +17,8 @@ #include "RenderArgs.h" #include "TextureCache.h" +#include "render/DrawStatus.h" + #include #include "overlay3D_vert.h" @@ -49,6 +51,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() { ))); _jobs.push_back(Job(new CullItems::JobModel("CullOpaque", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortOpaque", _jobs.back().getOutput()))); + auto& renderedOpaques = _jobs.back().getOutput(); _jobs.push_back(Job(new DrawOpaqueDeferred::JobModel("DrawOpaqueDeferred", _jobs.back().getOutput()))); _jobs.push_back(Job(new DrawLight::JobModel("DrawLight"))); _jobs.push_back(Job(new ResetGLState::JobModel())); @@ -65,6 +68,11 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(new CullItems::JobModel("CullTransparent", _jobs.back().getOutput()))); _jobs.push_back(Job(new DepthSortItems::JobModel("DepthSortTransparent", _jobs.back().getOutput(), DepthSortItems(false)))); _jobs.push_back(Job(new DrawTransparentDeferred::JobModel("TransparentDeferred", _jobs.back().getOutput()))); + + _jobs.push_back(Job(new render::DrawStatus::JobModel("DrawStatus", renderedOpaques))); + _jobs.back().setEnabled(false); + _drawStatusJobIndex = _jobs.size() - 1; + _jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D"))); _jobs.push_back(Job(new ResetGLState::JobModel())); } @@ -85,6 +93,9 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend return; } + // Make sure we turn the displayItemStatus on/off + setDrawItemStatus(renderContext->_drawItemStatus); + renderContext->args->_context->syncCache(); for (auto job : _jobs) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 2f65c5ade6..3d11e97634 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -70,6 +70,11 @@ public: render::Jobs _jobs; + int _drawStatusJobIndex = -1; + + void setDrawItemStatus(bool draw) { if (_drawStatusJobIndex >= 0) { _jobs[_drawStatusJobIndex].setEnabled(draw); } } + bool doDrawItemStatus() const { if (_drawStatusJobIndex >= 0) { return _jobs[_drawStatusJobIndex].isEnabled(); } else { return false; } } + virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); }; diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp new file mode 100644 index 0000000000..90d167cc2a --- /dev/null +++ b/libraries/render/src/render/DrawStatus.cpp @@ -0,0 +1,174 @@ +// +// DrawStatus.cpp +// render/src/render +// +// Created by Niraj Venkat on 6/29/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 +#include + +#include "DrawStatus.h" + +#include +#include "gpu/GPULogging.h" + + +#include "gpu/Batch.h" +#include "gpu/Context.h" + +#include "ViewFrustum.h" +#include "RenderArgs.h" + +#include "drawItemBounds_vert.h" +#include "drawItemBounds_frag.h" +#include "drawItemStatus_vert.h" +#include "drawItemStatus_frag.h" + +using namespace render; + + + +const gpu::PipelinePointer& DrawStatus::getDrawItemBoundsPipeline() { + if (!_drawItemBoundsPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemBounds_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemBounds_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _drawItemBoundPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemBoundDimLoc = program->getUniforms().findLocation("inBoundDim"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setDepthTest(true, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + // Good to go add the brand new pipeline + _drawItemBoundsPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _drawItemBoundsPipeline; +} + +const gpu::PipelinePointer& DrawStatus::getDrawItemStatusPipeline() { + if (!_drawItemStatusPipeline) { + auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(drawItemStatus_vert))); + auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(drawItemStatus_frag))); + gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); + + gpu::Shader::BindingSet slotBindings; + gpu::Shader::makeProgram(*program, slotBindings); + + _drawItemStatusPosLoc = program->getUniforms().findLocation("inBoundPos"); + _drawItemStatusDimLoc = program->getUniforms().findLocation("inBoundDim"); + _drawItemStatusValueLoc = program->getUniforms().findLocation("inStatus"); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + state->setDepthTest(false, false, gpu::LESS_EQUAL); + + // Blend on transparent + state->setBlendFunction(true, + gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, + gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); + + // Good to go add the brand new pipeline + _drawItemStatusPipeline.reset(gpu::Pipeline::create(program, state)); + } + return _drawItemStatusPipeline; +} + +void DrawStatus::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { + assert(renderContext->args); + assert(renderContext->args->_viewFrustum); + RenderArgs* args = renderContext->args; + auto& scene = sceneContext->_scene; + + // FIrst thing, we collect the bound and the status for all the items we want to render + int nbItems = 0; + { + if (!_itemBounds) { + _itemBounds.reset(new gpu::Buffer()); + } + if (!_itemStatus) { + _itemStatus.reset(new gpu::Buffer()); + } + + _itemBounds->resize((inItems.size() * sizeof(AABox))); + _itemStatus->resize((inItems.size() * sizeof(glm::vec4))); + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + for (auto& item : inItems) { + if (!item.bounds.isInvalid()) { + if (!item.bounds.isNull()) { + (*itemAABox) = item.bounds; + } else { + (*itemAABox).setBox(item.bounds.getCorner(), 0.1f); + } + auto& itemScene = scene->getItem(item.id); + (*itemStatus) = itemScene.getStatusPackedValues(); + + nbItems++; + itemAABox++; + itemStatus++; + } + } + } + + if (nbItems == 0) { + return; + } + + // Allright, something to render let's do it + gpu::Batch batch; + + glm::mat4 projMat; + Transform viewMat; + args->_viewFrustum->evalProjectionMatrix(projMat); + args->_viewFrustum->evalViewTransform(viewMat); + if (args->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + viewMat.postScale(glm::vec3(-1.0f, 1.0f, 1.0f)); + } + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setModelTransform(Transform()); + + // bind the one gpu::Pipeline we need + batch.setPipeline(getDrawItemBoundsPipeline()); + + AABox* itemAABox = reinterpret_cast (_itemBounds->editData()); + glm::ivec4* itemStatus = reinterpret_cast (_itemStatus->editData()); + + const unsigned int VEC3_ADRESS_OFFSET = 3; + + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemBoundPosLoc, 1, (const GLfloat*) (itemAABox + i)); + batch._glUniform3fv(_drawItemBoundDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + + batch.draw(gpu::LINES, 24, 0); + } + + batch.setPipeline(getDrawItemStatusPipeline()); + for (int i = 0; i < nbItems; i++) { + batch._glUniform3fv(_drawItemStatusPosLoc, 1, (const GLfloat*) (itemAABox + i)); + batch._glUniform3fv(_drawItemStatusDimLoc, 1, ((const GLfloat*) (itemAABox + i)) + VEC3_ADRESS_OFFSET); + batch._glUniform4iv(_drawItemStatusValueLoc, 1, (const GLint*) (itemStatus + i)); + + batch.draw(gpu::TRIANGLES, 24, 0); + } + + // Before rendering the batch make sure we re in sync with gl state + args->_context->syncCache(); + renderContext->args->_context->syncCache(); + args->_context->render((batch)); +} \ No newline at end of file diff --git a/libraries/render/src/render/DrawStatus.h b/libraries/render/src/render/DrawStatus.h new file mode 100644 index 0000000000..ca4763d33b --- /dev/null +++ b/libraries/render/src/render/DrawStatus.h @@ -0,0 +1,43 @@ +// +// DrawStatus.h +// render/src/render +// +// Created by Niraj Venkat on 6/29/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_render_DrawStatus_h +#define hifi_render_DrawStatus_h + +#include "DrawTask.h" +#include "gpu/Batch.h" + +namespace render { + class DrawStatus { + int _drawItemBoundPosLoc = -1; + int _drawItemBoundDimLoc = -1; + int _drawItemStatusPosLoc = -1; + int _drawItemStatusDimLoc = -1; + int _drawItemStatusValueLoc = -1; + + gpu::Stream::FormatPointer _drawItemFormat; + gpu::PipelinePointer _drawItemBoundsPipeline; + gpu::PipelinePointer _drawItemStatusPipeline; + gpu::BufferPointer _itemBounds; + gpu::BufferPointer _itemStatus; + + public: + + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems); + + typedef Job::ModelI JobModel; + + const gpu::PipelinePointer& getDrawItemBoundsPipeline(); + const gpu::PipelinePointer& getDrawItemStatusPipeline(); + }; +} + +#endif // hifi_render_DrawStatus_h diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 46103f7803..6b028e6c4b 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -126,9 +126,10 @@ struct ItemBound { float _nearDepth = 0.0f; float _farDepth = 0.0f; ItemID _id = 0; + AABox _bounds; ItemBound() {} - ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id) {} + ItemBound(float centerDepth, float nearDepth, float farDepth, ItemID id, const AABox& bounds) : _centerDepth(centerDepth), _nearDepth(nearDepth), _farDepth(farDepth), _id(id), _bounds(bounds) {} }; struct FrontToBackSort { @@ -165,7 +166,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende auto bound = itemDetails.bounds; // item.getBound(); float distance = args->_viewFrustum->distanceToCamera(bound.calcCenter()); - itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id)); + itemBounds.emplace_back(ItemBound(distance, distance, distance, itemDetails.id, bound)); } // sort against Z @@ -179,7 +180,7 @@ void render::depthSortItems(const SceneContextPointer& sceneContext, const Rende // FInally once sorted result to a list of itemID for (auto& itemBound : itemBounds) { - outItems.emplace_back(itemBound._id); + outItems.emplace_back(ItemIDAndBounds(itemBound._id, itemBound._bounds)); } } diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 8a4d424005..a0139732f6 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -77,6 +77,9 @@ public: Job(const Job& other) : _concept(other._concept) {} ~Job(); + bool isEnabled() const { return _concept->isEnabled(); } + void setEnabled(bool isEnabled) { _concept->setEnabled(isEnabled); } + const std::string& getName() const { return _concept->getName(); } const Varying getInput() const { return _concept->getInput(); } const Varying getOutput() const { return _concept->getOutput(); } @@ -92,6 +95,7 @@ public: class Concept { std::string _name; + bool _isEnabled = true; public: Concept() : _name() {} Concept(const std::string& name) : _name(name) {} @@ -99,7 +103,10 @@ public: void setName(const std::string& name) { _name = name; } const std::string& getName() const { return _name; } - + + bool isEnabled() const { return _isEnabled; } + void setEnabled(bool isEnabled) { _isEnabled = isEnabled; } + virtual const Varying getInput() const { return Varying(); } virtual const Varying getOutput() const { return Varying(); } virtual void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) = 0; @@ -119,7 +126,11 @@ public: Model(Data data): _data(data) {} Model(Data data, const std::string& name): Concept(name), _data(data) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRun(_data, sceneContext, renderContext); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRun(_data, sceneContext, renderContext); + } + } }; template class ModelI : public Concept { @@ -135,7 +146,11 @@ public: ModelI(const std::string& name, const Varying& input): Concept(name), _input(input) {} ModelI(const std::string& name, Data data): Concept(name), _data(data) {} - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRunI(_data, sceneContext, renderContext, _input.get()); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunI(_data, sceneContext, renderContext, _input.get()); + } + } }; template class ModelO : public Concept { @@ -155,7 +170,9 @@ public: ModelO(const std::string& name, Data data): Concept(name), _data(data), _output(Output()) {} void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { - jobRunO(_data, sceneContext, renderContext, _output.edit()); + if (isEnabled()) { + jobRunO(_data, sceneContext, renderContext, _output.edit()); + } } }; @@ -177,7 +194,11 @@ public: void setInput(const Varying& input) { _input = input; } - void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); } + void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) { + if (isEnabled()) { + jobRunIO(_data, sceneContext, renderContext, _input.get(), _output.edit()); + } + } }; std::shared_ptr _concept; diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 054f7e5ce4..1c600b13d6 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -49,6 +49,8 @@ public: int _numDrawnOverlay3DItems = 0; int _maxDrawnOverlay3DItems = -1; + bool _drawItemStatus = false; + RenderContext() {} }; typedef std::shared_ptr RenderContextPointer; diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index 1d2e54541b..a7145af4b5 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -10,6 +10,8 @@ // #include "Scene.h" +#include + using namespace render; void ItemBucketMap::insert(const ItemID& id, const ItemKey& key) { @@ -53,6 +55,50 @@ void ItemBucketMap::allocateStandardOpaqueTranparentBuckets() { (*this)[ItemFilter::Builder::transparentShape().withLayered()]; } +const Item::Status::Value Item::Status::Value::INVALID = Item::Status::Value(); + +const float Item::Status::Value::RED = 0.0f; +const float Item::Status::Value::YELLOW = 60.0f; +const float Item::Status::Value::GREEN = 120.0f; +const float Item::Status::Value::CYAN = 180.0f; +const float Item::Status::Value::BLUE = 240.0f; +const float Item::Status::Value::MAGENTA = 300.0f; + +void Item::Status::Value::setScale(float scale) { + _scale = (std::numeric_limits::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f)); + } + +void Item::Status::Value::setColor(float hue) { + // Convert the HUe from range [0, 360] to signed normalized value + const float HUE_MAX = 360.0f; + _color = (std::numeric_limits::max() - 1) * 0.5f * (1.0f + std::max(std::min(hue, HUE_MAX), 0.0f) / HUE_MAX); +} + +void Item::Status::getPackedValues(glm::ivec4& values) const { + for (unsigned int i = 0; i < values.length(); i++) { + if (i < _values.size()) { + values[i] = _values[i]().getPackedData(); + } else { + values[i] = Value::INVALID.getPackedData(); + } + } +} + +void Item::PayloadInterface::addStatusGetter(const Status::Getter& getter) { + if (!_status) { + _status.reset(new Status()); + } + _status->addGetter(getter); +} + +void Item::PayloadInterface::addStatusGetters(const Status::Getters& getters) { + if (!_status) { + _status.reset(new Status()); + } + for (auto& g : getters) { + _status->addGetter(g); + } +} void Item::resetPayload(const PayloadPointer& payload) { if (!payload) { @@ -63,6 +109,15 @@ void Item::resetPayload(const PayloadPointer& payload) { } } +glm::ivec4 Item::getStatusPackedValues() const { + glm::ivec4 values(Status::Value::INVALID.getPackedData()); + auto& status = getStatus(); + if (status) { + status->getPackedValues(values); + }; + return values; +} + void PendingChanges::resetItem(ItemID id, const PayloadPointer& payload) { _resetItems.push_back(id); _resetPayloads.push_back(payload); diff --git a/libraries/render/src/render/Scene.h b/libraries/render/src/render/Scene.h index 5ec9f0c951..934b460e52 100644 --- a/libraries/render/src/render/Scene.h +++ b/libraries/render/src/render/Scene.h @@ -196,12 +196,50 @@ public: // Bound is the AABBox fully containing this item typedef AABox Bound; - // Stats records the life history and performances of this item while performing at rendering and updating. + // Status records the life history and performances of this item while performing at rendering and updating. // This is Used for monitoring and dynamically adjust the quality - class Stats { + class Status { public: - int _firstFrame; + + // Status::Value class is the data used to represent the transient information of a status as a square icon + // The "icon" is a square displayed in the 3D scene over the render::Item AABB center. + // It can be scaled in the range [0, 1] and the color hue in the range [0, 360] representing the color wheel hue + class Value { + unsigned short _scale = 0xFFFF; + unsigned short _color = 0xFFFF; + public: + const static Value INVALID; // Invalid value meanss the status won't show + + Value() {} + Value(float scale, float hue) { setScale(scale); setColor(hue); } + + // It can be scaled in the range [0, 1] + void setScale(float scale); + // the color hue in the range [0, 360] representing the color wheel hue + void setColor(float hue); + + // Standard color Hue + static const float RED; // 0.0f; + static const float YELLOW; // 60.0f; + static const float GREEN; // 120.0f; + static const float CYAN; // 180.0f; + static const float BLUE; // 240.0f; + static const float MAGENTA; // 300.0f; + + // Retreive the Value data tightely packed as an int + int getPackedData() const { return *((const int*) this); } + }; + + typedef std::function Getter; + typedef std::vector Getters; + + Getters _values; + + void addGetter(const Getter& getter) { _values.push_back(getter); } + + void getPackedValues(glm::ivec4& values) const; }; + typedef std::shared_ptr StatusPointer; // Update Functor class UpdateFunctorInterface { @@ -222,7 +260,15 @@ public: virtual const model::MaterialKey getMaterialKey() const = 0; ~PayloadInterface() {} + + // Status interface is local to the base class + const StatusPointer& getStatus() const { return _status; } + void addStatusGetter(const Status::Getter& getter); + void addStatusGetters(const Status::Getters& getters); + protected: + StatusPointer _status; + friend class Item; virtual void update(const UpdateFunctorPointer& functor) = 0; }; @@ -253,6 +299,10 @@ public: // Shape Type Interface const model::MaterialKey getMaterialKey() const { return _payload->getMaterialKey(); } + // Access the status + const StatusPointer& getStatus() const { return _payload->getStatus(); } + glm::ivec4 getStatusPackedValues() const; + protected: PayloadPointer _payload; ItemKey _key; diff --git a/libraries/render/src/render/drawItemBounds.slf b/libraries/render/src/render/drawItemBounds.slf new file mode 100644 index 0000000000..b5d1a297bf --- /dev/null +++ b/libraries/render/src/render/drawItemBounds.slf @@ -0,0 +1,19 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// drawItemBounds.frag +// fragment shader +// +// Created by Sam Gateau on 6/29/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 +// + +varying vec4 varColor; + + +void main(void) { + gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); +} diff --git a/libraries/render/src/render/drawItemBounds.slv b/libraries/render/src/render/drawItemBounds.slv new file mode 100644 index 0000000000..4cb9a82371 --- /dev/null +++ b/libraries/render/src/render/drawItemBounds.slv @@ -0,0 +1,57 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// drawItemBounds.slv +// vertex shader +// +// Created by Sam Gateau on 6/29/2015. +// 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +uniform vec3 inBoundPos; +uniform vec3 inBoundDim; + +void main(void) { + const vec4 UNIT_BOX[8] = vec4[8]( + vec4(0.0, 0.0, 0.0, 1.0), + vec4(1.0, 0.0, 0.0, 1.0), + vec4(0.0, 1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0), + vec4(0.0, 0.0, 1.0, 1.0), + vec4(1.0, 0.0, 1.0, 1.0), + vec4(0.0, 1.0, 1.0, 1.0), + vec4(1.0, 1.0, 1.0, 1.0) + ); + const int UNIT_BOX_LINE_INDICES[24] = int[24]( + 0, 1, + 1, 3, + 3, 2, + 2, 0, + 4, 5, + 5, 7, + 7, 6, + 6, 4, + 2, 6, + 3, 7, + 0, 4, + 1, 5 + ); + vec4 pos = UNIT_BOX[UNIT_BOX_LINE_INDICES[gl_VertexID]]; + + pos.xyz = inBoundPos + inBoundDim * pos.xyz; + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, pos, gl_Position)$> + + // varTexcoord = (pos.xy + 1) * 0.5; +} \ No newline at end of file diff --git a/libraries/render/src/render/drawItemStatus.slf b/libraries/render/src/render/drawItemStatus.slf new file mode 100644 index 0000000000..dcf5d3ee25 --- /dev/null +++ b/libraries/render/src/render/drawItemStatus.slf @@ -0,0 +1,19 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// drawItemStatus.frag +// fragment shader +// +// Created by Sam Gateau on 6/30/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 +// + +varying vec4 varColor; + + +void main(void) { + gl_FragColor = varColor; +} diff --git a/libraries/render/src/render/drawItemStatus.slv b/libraries/render/src/render/drawItemStatus.slv new file mode 100644 index 0000000000..9e2b4919ff --- /dev/null +++ b/libraries/render/src/render/drawItemStatus.slv @@ -0,0 +1,101 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// drawItemStatus.slv +// vertex shader +// +// Created by Sam Gateau on 6/30/2015. +// 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 gpu/Transform.slh@> + +<$declareStandardTransform()$> + +varying vec4 varColor; + +uniform vec3 inBoundPos; +uniform vec3 inBoundDim; +uniform ivec4 inStatus; + +vec3 paintRainbow(float nv) { + float v = nv * 5.f; + if (v < 0.f) { + return vec3(0.f, 0.f, 0.f); + } else if (v < 1.f) { + return vec3(1.f, v, 0.f); + } else if (v < 2.f) { + return vec3(1.f - (v-1.f), 1.f, 0.f); + } else if (v < 3.f) { + return vec3(0.f, 1.f, (v-2.f)); + } else if (v < 4.f) { + return vec3(0.f, 1.f - (v-3.f), 1.f ); + } else if (v < 5.f) { + return vec3((v-4.f), 0.f, 1.f ); + } else { + return vec3(1.f, 1.f, 1.f); + } +} + +vec2 unpackStatus(int v) { + return vec2(clamp(float(int((v >> 0) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0), + clamp(float(int((v >> 16) & 0xFFFF) - 32727) / 32727.0, -1.0, 1.0)); +} + +void main(void) { + const vec2 ICON_PIXEL_SIZE = vec2(10, 10); + const vec2 MARGIN_PIXEL_SIZE = vec2(2, 2); + const int NUM_VERTICES = 6; + const vec4 UNIT_QUAD[NUM_VERTICES] = vec4[NUM_VERTICES]( + vec4(-1.0, -1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4(1.0, -1.0, 0.0, 1.0), + vec4(1.0, 1.0, 0.0, 1.0) + ); + + // anchor point in clip space + vec4 anchorPoint = vec4(inBoundPos, 1.0) + vec4(inBoundDim, 0.0) * vec4(0.5, 0.5, 0.5, 0.0); + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToClipPos(cam, obj, anchorPoint, anchorPoint)$> + + // Which icon are we dealing with ? + int iconNum = gl_VertexID / NUM_VERTICES; + + // if invalid, just kill + if (inStatus[iconNum] == 0xFFFFFFFF) { + gl_Position = anchorPoint; + varColor = vec4(1.0); + return; + } + + // unpack to get x and y satus + vec2 iconStatus = unpackStatus(inStatus[iconNum]); + + // Use the status for showing a color + varColor = vec4(paintRainbow(abs(iconStatus.y)), 1.0); + + // Also changes the size of the notification + vec2 iconScale = ICON_PIXEL_SIZE; + iconScale = max(vec2(1, 1), (iconScale * iconStatus.x)); + + //Offset icon to the right based on the iconNum + vec2 offset = vec2(iconNum * (ICON_PIXEL_SIZE.x + MARGIN_PIXEL_SIZE.x), 0); + + // Final position in pixel space + int twoTriID = gl_VertexID - iconNum * NUM_VERTICES; + vec4 pos = UNIT_QUAD[twoTriID]; + vec2 quadPixelPos = offset.xy + pos.xy * 0.5 * iconScale; + + vec4 viewport; + <$transformCameraViewport(cam, viewport)$>; + vec2 pixelToClip = vec2(2.0 / viewport.z, 2.0 / viewport.w); + gl_Position = anchorPoint + (anchorPoint.w * vec4(quadPixelPos * pixelToClip, 0.0, 0.0)); + +} \ No newline at end of file diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 8359aa58fa..674b452528 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -106,6 +106,10 @@ public: Q_INVOKABLE int getEngineMaxDrawnTransparentItems() { return _maxDrawnTransparentItems; } Q_INVOKABLE void setEngineMaxDrawnOverlay3DItems(int count) { _maxDrawnOverlay3DItems = count; } Q_INVOKABLE int getEngineMaxDrawnOverlay3DItems() { return _maxDrawnOverlay3DItems; } + + Q_INVOKABLE void setEngineDisplayItemStatus(bool display) { _drawItemStatus = display; } + Q_INVOKABLE bool doEngineDisplayItemStatus() { return _drawItemStatus; } + signals: void shouldRenderAvatarsChanged(bool shouldRenderAvatars); void shouldRenderEntitiesChanged(bool shouldRenderEntities); @@ -136,6 +140,8 @@ protected: int _maxDrawnTransparentItems = -1; int _maxDrawnOverlay3DItems = -1; + bool _drawItemStatus = false; + }; #endif // hifi_SceneScriptingInterface_h