From cf08a4162a399f13e42de830607ced6d8470bc8c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 11 Nov 2019 16:50:59 -0800 Subject: [PATCH] add workload job to help debug entity proxies --- .../src/raypick/PickScriptingInterface.h | 4 + .../scripting/SelectionScriptingInterface.cpp | 4 +- .../scripting/SelectionScriptingInterface.h | 4 +- interface/src/workload/GameWorkload.cpp | 2 + .../src/workload/GameWorkloadRenderer.cpp | 1 + .../src/workload/SelectedWorkloadRenderer.cpp | 88 +++++++++++++ .../src/workload/SelectedWorkloadRenderer.h | 32 +++++ libraries/entities/src/EntityTreeElement.cpp | 4 +- libraries/shared/src/PickFilter.h | 5 + libraries/workload/src/workload/Space.cpp | 12 ++ libraries/workload/src/workload/Space.h | 1 + .../debugging/debugWorkloadWithMouseHover.js | 124 ++++++++++++++++++ 12 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 interface/src/workload/SelectedWorkloadRenderer.cpp create mode 100644 interface/src/workload/SelectedWorkloadRenderer.h create mode 100644 scripts/developer/debugging/debugWorkloadWithMouseHover.js diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h index e26b91b9a2..5099156b37 100644 --- a/interface/src/raypick/PickScriptingInterface.h +++ b/interface/src/raypick/PickScriptingInterface.h @@ -87,6 +87,8 @@ class PickScriptingInterface : public QObject, public Dependency { Q_PROPERTY(unsigned int PICK_ALL_INTERSECTIONS READ PICK_ALL_INTERSECTIONS CONSTANT) + Q_PROPERTY(unsigned int PICK_BYPASS_IGNORE READ PICK_BYPASS_IGNORE CONSTANT) + Q_PROPERTY(unsigned int INTERSECTED_NONE READ INTERSECTED_NONE CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_ENTITY READ INTERSECTED_ENTITY CONSTANT) Q_PROPERTY(unsigned int INTERSECTED_LOCAL_ENTITY READ INTERSECTED_LOCAL_ENTITY CONSTANT) @@ -282,6 +284,8 @@ public: unsigned int getPerFrameTimeBudget() const; void setPerFrameTimeBudget(unsigned int numUsecs); + static constexpr unsigned int PICK_BYPASS_IGNORE() { return PickFilter::getBitMask(PickFilter::FlagBit::PICK_BYPASS_IGNORE); } + public slots: /**jsdoc diff --git a/interface/src/scripting/SelectionScriptingInterface.cpp b/interface/src/scripting/SelectionScriptingInterface.cpp index d2147ac5cc..32f837668d 100644 --- a/interface/src/scripting/SelectionScriptingInterface.cpp +++ b/interface/src/scripting/SelectionScriptingInterface.cpp @@ -17,7 +17,7 @@ GameplayObjects::GameplayObjects() { } bool GameplayObjects::addToGameplayObjects(const QUuid& avatarID) { - containsData = true; + _containsData = true; if (std::find(_avatarIDs.begin(), _avatarIDs.end(), avatarID) == _avatarIDs.end()) { _avatarIDs.push_back(avatarID); } @@ -29,7 +29,7 @@ bool GameplayObjects::removeFromGameplayObjects(const QUuid& avatarID) { } bool GameplayObjects::addToGameplayObjects(const EntityItemID& entityID) { - containsData = true; + _containsData = true; if (std::find(_entityIDs.begin(), _entityIDs.end(), entityID) == _entityIDs.end()) { _entityIDs.push_back(entityID); } diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h index 4386ee5ee6..f477a25b42 100644 --- a/interface/src/scripting/SelectionScriptingInterface.h +++ b/interface/src/scripting/SelectionScriptingInterface.h @@ -26,7 +26,7 @@ class GameplayObjects { public: GameplayObjects(); - bool getContainsData() const { return containsData; } + bool getContainsData() const { return _containsData; } std::vector getAvatarIDs() const { return _avatarIDs; } bool addToGameplayObjects(const QUuid& avatarID); @@ -37,7 +37,7 @@ public: bool removeFromGameplayObjects(const EntityItemID& entityID); private: - bool containsData { false }; + bool _containsData { false }; std::vector _avatarIDs; std::vector _entityIDs; }; diff --git a/interface/src/workload/GameWorkload.cpp b/interface/src/workload/GameWorkload.cpp index afbd166c89..d9cda7f16a 100644 --- a/interface/src/workload/GameWorkload.cpp +++ b/interface/src/workload/GameWorkload.cpp @@ -9,6 +9,7 @@ // #include "GameWorkload.h" #include "GameWorkloadRenderer.h" +#include "SelectedWorkloadRenderer.h" #include #include #include @@ -35,6 +36,7 @@ public: model.addJob("PhysicsBoundary", regionTrackerOut); model.addJob("SpaceToRender"); + model.addJob("SelectedWorkloadRender"); out = regionTrackerOut; } diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index 2bb73999f1..f65bf88754 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -16,6 +16,7 @@ #include #include +#include "SelectedWorkloadRenderer.h" void GameSpaceToRender::configure(const Config& config) { _freezeViews = config.freezeViews; diff --git a/interface/src/workload/SelectedWorkloadRenderer.cpp b/interface/src/workload/SelectedWorkloadRenderer.cpp new file mode 100644 index 0000000000..29cdc46f35 --- /dev/null +++ b/interface/src/workload/SelectedWorkloadRenderer.cpp @@ -0,0 +1,88 @@ +// +// SelectedWorkloadRenderer.cpp +// +// Created by Andrew Meadows 2019.11.08 +// Copyright 2019 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 "SelectedWorkloadRenderer.h" + +#include +#include + +#include + +#include "Application.h" +#include "GameWorkloadRenderer.h" +#include "scripting/SelectionScriptingInterface.h" + +void SelectedWorkloadRenderer::run(const workload::WorkloadContextPointer& runContext, Outputs& outputs) { + auto gameWorkloadContext = std::dynamic_pointer_cast(runContext); + if (!gameWorkloadContext) { + return; + } + auto space = gameWorkloadContext->_space; + if (!space) { + return; + } + + render::Transaction transaction; + auto scene = gameWorkloadContext->_scene; + + auto selection = DependencyManager::get(); + // Note: the "DebugWorkloadSelection" name is a secret hard-coded C++ debug feature. + // If you create such a named list using JS and the "Selection" API then it will be picked up here + // and the workload proxies for corresponding entities will be rendered. + GameplayObjects selectedObjects = selection->getList("DebugWorkloadSelection"); + + if (!selectedObjects.getContainsData()) { + // nothing to render + // clear item if it exists and bail + if (render::Item::isValidID(_spaceRenderItemID)) { + transaction.updateItem(_spaceRenderItemID, [](GameWorkloadRenderItem& item) { + item.setVisible(false); + }); + scene->enqueueTransaction(transaction); + } + return; + } + + std::vector entityIDs = selectedObjects.getEntityIDs(); + workload::indexed_container::Indices indices; + indices.reserve(entityIDs.size()); + + auto entityTreeRenderer = qApp->getEntities(); + auto entityTree = entityTreeRenderer->getTree(); + for (auto id : entityIDs) { + EntityItemPointer entity = entityTree->findEntityByID(id); + if (entity) { + indices.push_back(entity->getSpaceIndex()); + } + } + + workload::Proxy::Vector proxies; + proxies.reserve(indices.size()); + space->copySelectedProxyValues(proxies, indices); + + if (!render::Item::isValidID(_spaceRenderItemID)) { + _spaceRenderItemID = scene->allocateID(); + auto renderItem = std::make_shared(); + renderItem->editBound().setBox(glm::vec3(-16000.0f), 32000.0f); + transaction.resetItem(_spaceRenderItemID, std::make_shared(renderItem)); + } + + bool showProxies = true; + bool showViews = false; + bool visible = true; + workload::Views views(0); + transaction.updateItem(_spaceRenderItemID, [visible, showProxies, proxies, showViews, views](GameWorkloadRenderItem& item) { + item.setVisible(visible); + item.showProxies(showProxies); + item.setAllProxies(proxies); + item.showViews(showViews); + item.setAllViews(views); + }); + scene->enqueueTransaction(transaction); +} diff --git a/interface/src/workload/SelectedWorkloadRenderer.h b/interface/src/workload/SelectedWorkloadRenderer.h new file mode 100644 index 0000000000..9b3ac1005e --- /dev/null +++ b/interface/src/workload/SelectedWorkloadRenderer.h @@ -0,0 +1,32 @@ +// +// GameWorkloadRender.h +// +// Created by Sam Gateau on 2/20/2018. +// Copyright 2018 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_SelectedWorkloadRenderer_h +#define hifi_SelectedWorkloadRenderer_h + +#include "GameWorkload.h" + +#include "GameWorkloadRenderer.h" + +class SelectedWorkloadRenderer { +public: + using Config = GameSpaceToRenderConfig; + using Outputs = render::Transaction; + using JobModel = workload::Job::ModelO; + + SelectedWorkloadRenderer() {} + + void configure(const Config& config) {} + void run(const workload::WorkloadContextPointer& renderContext, Outputs& outputs); + +protected: + render::ItemID _spaceRenderItemID{ render::Item::INVALID_ITEM_ID }; +}; + +#endif diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 60eaafc0dd..992dcd9d05 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -197,7 +197,7 @@ EntityItemID EntityTreeElement::evalDetailedRayIntersection(const glm::vec3& ori // only called if we do intersect our bounding cube, but find if we actually intersect with entities... EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { - if (entity->getIgnorePickIntersection()) { + if (entity->getIgnorePickIntersection() && !searchFilter.bypassIgnore()) { return; } @@ -341,7 +341,7 @@ EntityItemID EntityTreeElement::evalDetailedParabolaIntersection(const glm::vec3 // only called if we do intersect our bounding cube, but find if we actually intersect with entities... EntityItemID entityID; forEachEntity([&](EntityItemPointer entity) { - if (entity->getIgnorePickIntersection()) { + if (entity->getIgnorePickIntersection() && !searchFilter.bypassIgnore()) { return; } diff --git a/libraries/shared/src/PickFilter.h b/libraries/shared/src/PickFilter.h index 5e5f5db3bc..d3d6673f50 100644 --- a/libraries/shared/src/PickFilter.h +++ b/libraries/shared/src/PickFilter.h @@ -10,6 +10,7 @@ #define hifi_PickFilter_h #include +#include // adebug class PickFilter { public: @@ -60,6 +61,8 @@ public: // NOT YET IMPLEMENTED PICK_ALL_INTERSECTIONS, // if not set, returns closest intersection, otherwise, returns list of all intersections + PICK_BYPASS_IGNORE, // for debug purposes + NUM_FLAGS, // Not a valid flag }; typedef std::bitset Flags; @@ -93,6 +96,8 @@ public: bool doesWantAllIntersections() const { return _flags[PICK_ALL_INTERSECTIONS]; } + bool bypassIgnore() const { return _flags[PICK_BYPASS_IGNORE]; } + // Helpers for RayPickManager Flags getEntityFlags() const { unsigned int toReturn = 0; diff --git a/libraries/workload/src/workload/Space.cpp b/libraries/workload/src/workload/Space.cpp index f045c8311f..5704ba8c4d 100644 --- a/libraries/workload/src/workload/Space.cpp +++ b/libraries/workload/src/workload/Space.cpp @@ -127,6 +127,18 @@ uint32_t Space::copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const { return numCopied; } +uint32_t Space::copySelectedProxyValues(Proxy::Vector& proxies, const workload::indexed_container::Indices& indices) const { + std::unique_lock lock(_proxiesMutex); + uint32_t numCopied = 0; + for (auto index : indices) { + if (isAllocatedID(index) && (index < (Index)_proxies.size())) { + proxies.push_back(_proxies[index]); + ++numCopied; + } + } + return numCopied; +} + const Owner Space::getOwner(int32_t proxyID) const { std::unique_lock lock(_proxiesMutex); if (isAllocatedID(proxyID) && (proxyID < (Index)_proxies.size())) { diff --git a/libraries/workload/src/workload/Space.h b/libraries/workload/src/workload/Space.h index 7dcb2217f7..d189d48156 100644 --- a/libraries/workload/src/workload/Space.h +++ b/libraries/workload/src/workload/Space.h @@ -47,6 +47,7 @@ public: void categorizeAndGetChanges(std::vector& changes); uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const; + uint32_t copySelectedProxyValues(Proxy::Vector& proxies, const workload::indexed_container::Indices& indices) const; const Owner getOwner(int32_t proxyID) const; uint8_t getRegion(int32_t proxyID) const; diff --git a/scripts/developer/debugging/debugWorkloadWithMouseHover.js b/scripts/developer/debugging/debugWorkloadWithMouseHover.js new file mode 100644 index 0000000000..eaff607359 --- /dev/null +++ b/scripts/developer/debugging/debugWorkloadWithMouseHover.js @@ -0,0 +1,124 @@ +// +// debugWorkloadWithMouseHover.js - render workload proxy for entity under mouse hover +// +// Copyright 2019 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 +// + +"use strict"; + +(function() { + + Script.scriptEnding.connect(function () { + }); + + // Create a Laser pointer used to pick and add entity to selection + var END_DIMENSIONS = { x: 0.05, y: 0.05, z: 0.05 }; + var COLOR1 = {red: 255, green: 0, blue: 255}; // magenta + var COLOR2 = {red: 255, green: 255, blue: 0}; // yellow + var end1 = { + type: "sphere", + dimensions: END_DIMENSIONS, + color: COLOR1, + ignorePickIntersection: true + } + var end2 = { + type: "sphere", + dimensions: END_DIMENSIONS, + color: COLOR2, + ignorePickIntersection: true + } + var laser = Pointers.createPointer(PickType.Ray, { + joint: "Mouse", + filter: Picks.PICK_ENTITIES | Picks.PICK_BYPASS_IGNORE | Picks.PICK_INCLUDE_COLLIDABLE | Picks.PICK_INCLUDE_NONCOLLIDABLE, + renderStates: [{name: "one", end: end1}], + defaultRenderStates: [{name: "one", end: end2, distance: 2.0}], + enabled: true + }); + Pointers.setRenderState(laser, "one"); + var hoveredObject = undefined; + + var SelectionListName = "DebugWorkloadSelection"; // sekret undocumented selection list (hard coded in C++) + var selectionStyle = { + isOutlineSmooth: true, + outlineWidth: 5, + outlineUnoccludedColor: {red: 255, green: 128, blue: 128}, + outlineUnoccludedAlpha: 0.88, + outlineOccludedColor: {red: 255, green: 128, blue: 128}, + outlineOccludedAlpha:0.5, + fillUnoccludedColor: {red: 26, green: 0, blue: 0}, + fillUnoccludedAlpha: 0.0, + fillOccludedColor: {red: 26, green: 0, blue: 0}, + fillOccludedAlpha: 0.0 + } + Selection.enableListHighlight(SelectionListName, selectionStyle) + + var isSelectionEnabled = false + + function setSelectionEnabled(enabled) { + if (isSelectionEnabled != enabled) { + isSelectionEnabled = enabled; + //print("isSelectionEnabled set to " + isSelectionEnabled.toString()) + if (isSelectionEnabled) { + Pointers.enablePointer(laser) + } else { + Pointers.disablePointer(laser) + Selection.clearSelectedItemsList(SelectionListName) + } + } + } + setSelectionEnabled(true); + + function getIntersectionTypeString(type) { + if (type === Picks.INTERSECTED_ENTITY) { + return "entity"; + } else if (type === Picks.INTERSECTED_OVERLAY) { + return "overlay"; + } else if (type === Picks.INTERSECTED_AVATAR) { + return "avatar"; + } + } + + function update() { + var result = Pointers.getPrevPickResult(laser); + if (result.intersects) { + if (hoveredObject !== undefined && result.objectID !== hoveredObject.objectID) { + // Hovering on something different + if (isSelectionEnabled) { + Selection.removeFromSelectedItemsList(SelectionListName, getIntersectionTypeString(hoveredObject.type), hoveredObject.objectID) + //print("remove helloDebugHighlight " + hoveredObject.objectID.toString()); + } + } + + if (isSelectionEnabled) { + if (hoveredObject === undefined || result.objectID !== hoveredObject.objectID) { + // Hovering over something new + Selection.addToSelectedItemsList(SelectionListName, getIntersectionTypeString(result.type), result.objectID); + hoveredObject = result; + //print("add helloDebugHighlight " + hoveredObject.objectID.toString() + " type = '" + getIntersectionTypeString(result.type) + "'"); + } + } + } else if (hoveredObject !== undefined) { + // Stopped hovering + if (isSelectionEnabled) { + Selection.removeFromSelectedItemsList(SelectionListName, getIntersectionTypeString(hoveredObject.type), hoveredObject.objectID) + hoveredObject = undefined; + //print("clear helloDebugHighlight"); + } + } + } + Script.update.connect(update); + + function cleanup() { + Pointers.removePointer(laser); + Selection.disableListHighlight(SelectionListName) + Selection.removeListFromMap(SelectionListName) + + } + Script.scriptEnding.connect(cleanup); + +}()); + +