mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 07:27:04 +02:00
Merge pull request #13303 from highfidelity/workload
workload project part 1
This commit is contained in:
commit
4424a8df11
70 changed files with 3681 additions and 371 deletions
|
@ -206,14 +206,13 @@ endif()
|
||||||
|
|
||||||
# link required hifi libraries
|
# link required hifi libraries
|
||||||
link_hifi_libraries(
|
link_hifi_libraries(
|
||||||
shared task octree ktx gpu gl procedural graphics graphics-scripting render
|
shared workload task octree ktx gpu gl procedural graphics graphics-scripting render
|
||||||
pointers
|
pointers
|
||||||
recording fbx networking model-networking entities avatars trackers
|
recording fbx networking model-networking entities avatars trackers
|
||||||
audio audio-client animation script-engine physics
|
audio audio-client animation script-engine physics
|
||||||
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
render-utils entities-renderer avatars-renderer ui qml auto-updater midi
|
||||||
controllers plugins image trackers
|
controllers plugins image trackers
|
||||||
ui-plugins display-plugins input-plugins
|
ui-plugins display-plugins input-plugins
|
||||||
workload
|
|
||||||
${PLATFORM_GL_BACKEND}
|
${PLATFORM_GL_BACKEND}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -2539,6 +2539,8 @@ Application::~Application() {
|
||||||
_main3DScene = nullptr;
|
_main3DScene = nullptr;
|
||||||
_renderEngine = nullptr;
|
_renderEngine = nullptr;
|
||||||
|
|
||||||
|
_gameWorkload.shutdown();
|
||||||
|
|
||||||
DependencyManager::destroy<Preferences>();
|
DependencyManager::destroy<Preferences>();
|
||||||
|
|
||||||
_entityClipboard->eraseAllOctreeElements();
|
_entityClipboard->eraseAllOctreeElements();
|
||||||
|
@ -2995,6 +2997,7 @@ void Application::onDesktopRootContextCreated(QQmlContext* surfaceContext) {
|
||||||
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
surfaceContext->setContextProperty("HMD", DependencyManager::get<HMDScriptingInterface>().data());
|
||||||
surfaceContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
surfaceContext->setContextProperty("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||||
surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
surfaceContext->setContextProperty("Render", _renderEngine->getConfiguration().get());
|
||||||
|
surfaceContext->setContextProperty("Workload", _gameWorkload._engine->getConfiguration().get());
|
||||||
surfaceContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
|
surfaceContext->setContextProperty("Reticle", getApplicationCompositor().getReticleInterface());
|
||||||
surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
|
surfaceContext->setContextProperty("Snapshot", DependencyManager::get<Snapshot>().data());
|
||||||
|
|
||||||
|
@ -4534,6 +4537,10 @@ void Application::idle() {
|
||||||
qFatal("Unable to make main thread context current");
|
qFatal("Unable to make main thread context current");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
_gameWorkload.updateViews(_viewFrustum, getMyAvatar()->getHeadPosition());
|
||||||
|
_gameWorkload._engine->run();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
PerformanceTimer perfTimer("update");
|
PerformanceTimer perfTimer("update");
|
||||||
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
PerformanceWarning warn(showWarnings, "Application::idle()... update()");
|
||||||
|
@ -4929,6 +4936,9 @@ void Application::init() {
|
||||||
avatar->setCollisionSound(sound);
|
avatar->setCollisionSound(sound);
|
||||||
}
|
}
|
||||||
}, Qt::QueuedConnection);
|
}, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
_gameWorkload.startup(getEntities()->getWorkloadSpace(), _main3DScene, _entitySimulation);
|
||||||
|
_entitySimulation->setWorkloadSpace(getEntities()->getWorkloadSpace());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::loadAvatarScripts(const QVector<QString>& urls) {
|
void Application::loadAvatarScripts(const QVector<QString>& urls) {
|
||||||
|
@ -5280,14 +5290,16 @@ void Application::setKeyboardFocusEntity(const EntityItemID& entityItemID) {
|
||||||
auto entityId = _keyboardFocusedEntity.get();
|
auto entityId = _keyboardFocusedEntity.get();
|
||||||
if (entities->wantsKeyboardFocus(entityId)) {
|
if (entities->wantsKeyboardFocus(entityId)) {
|
||||||
entities->setProxyWindow(entityId, _window->windowHandle());
|
entities->setProxyWindow(entityId, _window->windowHandle());
|
||||||
auto entity = getEntities()->getEntity(entityId);
|
|
||||||
if (_keyboardMouseDevice->isActive()) {
|
if (_keyboardMouseDevice->isActive()) {
|
||||||
_keyboardMouseDevice->pluginFocusOutEvent();
|
_keyboardMouseDevice->pluginFocusOutEvent();
|
||||||
}
|
}
|
||||||
_lastAcceptedKeyPress = usecTimestampNow();
|
_lastAcceptedKeyPress = usecTimestampNow();
|
||||||
|
|
||||||
setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
|
auto entity = getEntities()->getEntity(entityId);
|
||||||
entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
|
if (entity) {
|
||||||
|
setKeyboardFocusHighlight(entity->getWorldPosition(), entity->getWorldOrientation(),
|
||||||
|
entity->getScaledDimensions() * FOCUS_HIGHLIGHT_EXPANSION_FACTOR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5615,7 +5627,10 @@ void Application::update(float deltaTime) {
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "Simulation");
|
PROFILE_RANGE(simulation_physics, "Simulation");
|
||||||
PerformanceTimer perfTimer("simulation");
|
PerformanceTimer perfTimer("simulation");
|
||||||
|
|
||||||
if (_physicsEnabled) {
|
if (_physicsEnabled) {
|
||||||
|
auto t0 = std::chrono::high_resolution_clock::now();
|
||||||
|
auto t1 = t0;
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
PROFILE_RANGE(simulation_physics, "PrePhysics");
|
||||||
PerformanceTimer perfTimer("prePhysics)");
|
PerformanceTimer perfTimer("prePhysics)");
|
||||||
|
@ -5639,6 +5654,8 @@ void Application::update(float deltaTime) {
|
||||||
|
|
||||||
_entitySimulation->applyDynamicChanges();
|
_entitySimulation->applyDynamicChanges();
|
||||||
|
|
||||||
|
t1 = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
avatarManager->getObjectsToRemoveFromPhysics(motionStates);
|
||||||
_physicsEngine->removeObjects(motionStates);
|
_physicsEngine->removeObjects(motionStates);
|
||||||
avatarManager->getObjectsToAddToPhysics(motionStates);
|
avatarManager->getObjectsToAddToPhysics(motionStates);
|
||||||
|
@ -5651,6 +5668,7 @@ void Application::update(float deltaTime) {
|
||||||
dynamic->prepareForPhysicsSimulation();
|
dynamic->prepareForPhysicsSimulation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
auto t2 = std::chrono::high_resolution_clock::now();
|
||||||
{
|
{
|
||||||
PROFILE_RANGE(simulation_physics, "StepPhysics");
|
PROFILE_RANGE(simulation_physics, "StepPhysics");
|
||||||
PerformanceTimer perfTimer("stepPhysics");
|
PerformanceTimer perfTimer("stepPhysics");
|
||||||
|
@ -5658,6 +5676,7 @@ void Application::update(float deltaTime) {
|
||||||
_physicsEngine->stepSimulation();
|
_physicsEngine->stepSimulation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
auto t3 = std::chrono::high_resolution_clock::now();
|
||||||
{
|
{
|
||||||
if (_physicsEngine->hasOutgoingChanges()) {
|
if (_physicsEngine->hasOutgoingChanges()) {
|
||||||
{
|
{
|
||||||
|
@ -5703,12 +5722,26 @@ void Application::update(float deltaTime) {
|
||||||
// NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework
|
// NOTE: the PhysicsEngine stats are written to stdout NOT to Qt log framework
|
||||||
_physicsEngine->dumpStatsIfNecessary();
|
_physicsEngine->dumpStatsIfNecessary();
|
||||||
}
|
}
|
||||||
|
auto t4 = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
if (!_aboutToQuit) {
|
if (!_aboutToQuit) {
|
||||||
// NOTE: the getEntities()->update() call below will wait for lock
|
// NOTE: the getEntities()->update() call below will wait for lock
|
||||||
// and will provide non-physical entity motion
|
// and will provide non-physical entity motion
|
||||||
getEntities()->update(true); // update the models...
|
getEntities()->update(true); // update the models...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto t5 = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
workload::Timings timings(6);
|
||||||
|
timings[0] = (t4 - t0);
|
||||||
|
timings[1] = (t5 - t4);
|
||||||
|
timings[2] = (t4 - t3);
|
||||||
|
timings[3] = (t3 - t2);
|
||||||
|
timings[4] = (t2 - t1);
|
||||||
|
timings[5] = (t1 - t0);
|
||||||
|
|
||||||
|
_gameWorkload.updateSimulationTimings(timings);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -6562,6 +6595,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
||||||
|
|
||||||
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||||
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
||||||
|
scriptEngine->registerGlobalObject("Workload", _gameWorkload._engine->getConfiguration().get());
|
||||||
|
|
||||||
GraphicsScriptingInterface::registerMetaTypes(scriptEngine.data());
|
GraphicsScriptingInterface::registerMetaTypes(scriptEngine.data());
|
||||||
scriptEngine->registerGlobalObject("Graphics", DependencyManager::get<GraphicsScriptingInterface>().data());
|
scriptEngine->registerGlobalObject("Graphics", DependencyManager::get<GraphicsScriptingInterface>().data());
|
||||||
|
|
|
@ -72,6 +72,8 @@
|
||||||
#include "ui/overlays/Overlays.h"
|
#include "ui/overlays/Overlays.h"
|
||||||
#include "UndoStackScriptingInterface.h"
|
#include "UndoStackScriptingInterface.h"
|
||||||
|
|
||||||
|
#include "workload/GameWorkload.h"
|
||||||
|
|
||||||
#include <procedural/ProceduralSkybox.h>
|
#include <procedural/ProceduralSkybox.h>
|
||||||
#include <graphics/Skybox.h>
|
#include <graphics/Skybox.h>
|
||||||
#include <ModelScriptingInterface.h>
|
#include <ModelScriptingInterface.h>
|
||||||
|
@ -274,6 +276,8 @@ public:
|
||||||
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
render::EnginePointer getRenderEngine() override { return _renderEngine; }
|
||||||
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
gpu::ContextPointer getGPUContext() const { return _gpuContext; }
|
||||||
|
|
||||||
|
const GameWorkload& getGameWorkload() const { return _gameWorkload; }
|
||||||
|
|
||||||
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
|
virtual void pushPostUpdateLambda(void* key, const std::function<void()>& func) override;
|
||||||
|
|
||||||
void updateMyAvatarLookAtPosition();
|
void updateMyAvatarLookAtPosition();
|
||||||
|
@ -657,6 +661,8 @@ private:
|
||||||
render::EnginePointer _renderEngine{ new render::RenderEngine() };
|
render::EnginePointer _renderEngine{ new render::RenderEngine() };
|
||||||
gpu::ContextPointer _gpuContext; // initialized during window creation
|
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||||
|
|
||||||
|
GameWorkload _gameWorkload;
|
||||||
|
|
||||||
mutable QMutex _renderArgsMutex{ QMutex::Recursive };
|
mutable QMutex _renderArgsMutex{ QMutex::Recursive };
|
||||||
struct AppRenderArgs {
|
struct AppRenderArgs {
|
||||||
render::Args _renderArgs;
|
render::Args _renderArgs;
|
||||||
|
|
|
@ -77,7 +77,7 @@ void addAvatarEntities(const QVariantList& avatarEntities) {
|
||||||
|
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
// since we're creating this object we will immediately volunteer to own its simulation
|
// since we're creating this object we will immediately volunteer to own its simulation
|
||||||
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||||
entityProperties.setLastEdited(entity->getLastEdited());
|
entityProperties.setLastEdited(entity->getLastEdited());
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree";
|
qCDebug(entities) << "AvatarEntitiesBookmark failed to add new Entity to local Octree";
|
||||||
|
|
|
@ -490,9 +490,9 @@ void Stats::updateStats(bool force) {
|
||||||
};
|
};
|
||||||
for (int32_t j = 0; j < categories.size(); ++j) {
|
for (int32_t j = 0; j < categories.size(); ++j) {
|
||||||
QString recordKey = "/idle/update/" + categories[j];
|
QString recordKey = "/idle/update/" + categories[j];
|
||||||
itr = allRecords.find(recordKey);
|
auto record = PerformanceTimer::getTimerRecord(recordKey);
|
||||||
if (itr != allRecords.end()) {
|
if (record.getCount()) {
|
||||||
float dt = (float)itr.value().getMovingAverage() / (float)USECS_PER_MSEC;
|
float dt = (float) record.getMovingAverage() / (float)USECS_PER_MSEC;
|
||||||
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
|
QString message = QString("\n %1 = %2").arg(categories[j]).arg(dt);
|
||||||
idleUpdateStats.push(SortableStat(message, dt));
|
idleUpdateStats.push(SortableStat(message, dt));
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,6 +254,7 @@ void Web3DOverlay::setupQmlSurface() {
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
_webSurface->getSurfaceContext()->setContextProperty("MenuInterface", MenuScriptingInterface::getInstance());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
_webSurface->getSurfaceContext()->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
_webSurface->getSurfaceContext()->setContextProperty("Render", AbstractViewStateInterface::instance()->getRenderEngine()->getConfiguration().get());
|
||||||
|
_webSurface->getSurfaceContext()->setContextProperty("Workload", qApp->getGameWorkload()._engine->getConfiguration().get());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
_webSurface->getSurfaceContext()->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
_webSurface->getSurfaceContext()->setContextProperty("Pointers", DependencyManager::get<PointerScriptingInterface>().data());
|
||||||
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
|
_webSurface->getSurfaceContext()->setContextProperty("Web3DOverlay", this);
|
||||||
|
|
82
interface/src/workload/GameWorkload.cpp
Normal file
82
interface/src/workload/GameWorkload.cpp
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
//
|
||||||
|
// GameWorkload.cpp
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 2/16/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
|
||||||
|
//
|
||||||
|
#include "GameWorkload.h"
|
||||||
|
#include "GameWorkloadRenderer.h"
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
#include <workload/RegionTracker.h>
|
||||||
|
#include <workload/SpaceClassifier.h>
|
||||||
|
|
||||||
|
#include "PhysicsBoundary.h"
|
||||||
|
|
||||||
|
class WorkloadEngineBuilder {
|
||||||
|
public:
|
||||||
|
using Inputs = workload::VaryingSet2<workload::Views, workload::Timings>;
|
||||||
|
using Outputs = workload::RegionTracker::Outputs;
|
||||||
|
using JobModel = workload::Task::ModelIO<WorkloadEngineBuilder, Inputs, Outputs>;
|
||||||
|
void build(JobModel& model, const workload::Varying& in, workload::Varying& out) {
|
||||||
|
|
||||||
|
const auto& inViews = in.getN<Inputs>(0);
|
||||||
|
const auto& inTimings = in.getN<Inputs>(1);
|
||||||
|
|
||||||
|
const auto usedViews = model.addJob<workload::SetupViews>("setupViews", inViews);
|
||||||
|
|
||||||
|
const auto controlViewsIn = workload::ControlViews::Input(usedViews, inTimings).asVarying();
|
||||||
|
const auto fixedViews = model.addJob<workload::ControlViews>("controlViews", controlViewsIn);
|
||||||
|
|
||||||
|
const auto regionTrackerOut = model.addJob<workload::SpaceClassifierTask>("spaceClassifier", fixedViews);
|
||||||
|
|
||||||
|
model.addJob<PhysicsBoundary>("PhysicsBoundary", regionTrackerOut);
|
||||||
|
|
||||||
|
model.addJob<GameSpaceToRender>("SpaceToRender");
|
||||||
|
|
||||||
|
out = regionTrackerOut;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
GameWorkloadContext::GameWorkloadContext(const workload::SpacePointer& space,
|
||||||
|
const render::ScenePointer& scene,
|
||||||
|
const PhysicalEntitySimulationPointer& simulation): WorkloadContext(space),
|
||||||
|
_scene(scene), _simulation(simulation)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GameWorkloadContext::~GameWorkloadContext() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GameWorkload::GameWorkload() :
|
||||||
|
_engine(std::make_shared<workload::Engine>(WorkloadEngineBuilder::JobModel::create("Workload"), std::shared_ptr<GameWorkloadContext>()))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GameWorkload::~GameWorkload() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkload::startup(const workload::SpacePointer& space,
|
||||||
|
const render::ScenePointer& scene,
|
||||||
|
const PhysicalEntitySimulationPointer& simulation) {
|
||||||
|
_engine->reset(std::make_shared<GameWorkloadContext>(space, scene, simulation));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkload::shutdown() {
|
||||||
|
_engine.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkload::updateViews(const ViewFrustum& frustum, const glm::vec3& headPosition) {
|
||||||
|
workload::Views views;
|
||||||
|
views.emplace_back(workload::View::evalFromFrustum(frustum, headPosition - frustum.getPosition()));
|
||||||
|
views.emplace_back(workload::View::evalFromFrustum(frustum));
|
||||||
|
_engine->feedInput<WorkloadEngineBuilder::Inputs>(0, views);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkload::updateSimulationTimings(const workload::Timings& timings) {
|
||||||
|
_engine->feedInput<WorkloadEngineBuilder::Inputs>(1, timings);
|
||||||
|
}
|
45
interface/src/workload/GameWorkload.h
Normal file
45
interface/src/workload/GameWorkload.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
//
|
||||||
|
// GameWorkload.h
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 2/16/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_GameWorkload_h
|
||||||
|
#define hifi_GameWorkload_h
|
||||||
|
|
||||||
|
#include <workload/Space.h>
|
||||||
|
#include <workload/Engine.h>
|
||||||
|
|
||||||
|
#include <render/Scene.h>
|
||||||
|
#include "PhysicalEntitySimulation.h"
|
||||||
|
|
||||||
|
class GameWorkloadContext : public workload::WorkloadContext {
|
||||||
|
public:
|
||||||
|
GameWorkloadContext(const workload::SpacePointer& space,
|
||||||
|
const render::ScenePointer& scene,
|
||||||
|
const PhysicalEntitySimulationPointer& simulation);
|
||||||
|
virtual ~GameWorkloadContext();
|
||||||
|
|
||||||
|
render::ScenePointer _scene;
|
||||||
|
PhysicalEntitySimulationPointer _simulation;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GameWorkload {
|
||||||
|
public:
|
||||||
|
GameWorkload();
|
||||||
|
~GameWorkload();
|
||||||
|
|
||||||
|
void startup(const workload::SpacePointer& space,
|
||||||
|
const render::ScenePointer& scene,
|
||||||
|
const PhysicalEntitySimulationPointer& simulation);
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
void updateViews(const ViewFrustum& frustum, const glm::vec3& headPosition);
|
||||||
|
void updateSimulationTimings(const workload::Timings& timings);
|
||||||
|
|
||||||
|
workload::EnginePointer _engine;
|
||||||
|
};
|
||||||
|
#endif // hifi_GameWorkload_h
|
257
interface/src/workload/GameWorkloadRenderer.cpp
Normal file
257
interface/src/workload/GameWorkloadRenderer.cpp
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
//
|
||||||
|
// GameWorkloadRender.cpp
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
#include "GameWorkloadRenderer.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <gpu/Context.h>
|
||||||
|
|
||||||
|
#include <StencilMaskPass.h>
|
||||||
|
#include <GeometryCache.h>
|
||||||
|
|
||||||
|
#include "render-utils/drawWorkloadProxy_vert.h"
|
||||||
|
#include "render-utils/drawWorkloadView_vert.h"
|
||||||
|
#include "render-utils/drawWorkloadProxy_frag.h"
|
||||||
|
#include "render-utils/drawWorkloadView_frag.h"
|
||||||
|
|
||||||
|
|
||||||
|
void GameSpaceToRender::configure(const Config& config) {
|
||||||
|
_freezeViews = config.freezeViews;
|
||||||
|
_showAllProxies = config.showProxies;
|
||||||
|
_showAllViews = config.showViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameSpaceToRender::run(const workload::WorkloadContextPointer& runContext, Outputs& outputs) {
|
||||||
|
auto gameWorkloadContext = std::dynamic_pointer_cast<GameWorkloadContext>(runContext);
|
||||||
|
if (!gameWorkloadContext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto space = gameWorkloadContext->_space;
|
||||||
|
if (!space) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto visible = _showAllProxies || _showAllViews;
|
||||||
|
auto showProxies = _showAllProxies;
|
||||||
|
auto showViews = _showAllViews;
|
||||||
|
auto freezeViews = _freezeViews;
|
||||||
|
|
||||||
|
render::Transaction transaction;
|
||||||
|
auto scene = gameWorkloadContext->_scene;
|
||||||
|
|
||||||
|
// Nothing really needed, early exit
|
||||||
|
if (!visible) {
|
||||||
|
if (render::Item::isValidID(_spaceRenderItemID)) {
|
||||||
|
transaction.updateItem<GameWorkloadRenderItem>(_spaceRenderItemID, [](GameWorkloadRenderItem& item) {
|
||||||
|
item.setVisible(false);
|
||||||
|
});
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
workload::Proxy::Vector proxies(space->getNumAllocatedProxies());
|
||||||
|
space->copyProxyValues(proxies.data(), (uint32_t)proxies.size());
|
||||||
|
|
||||||
|
workload::Views views(space->getNumViews());
|
||||||
|
space->copyViews(views);
|
||||||
|
|
||||||
|
// Valid space, let's display its content
|
||||||
|
if (!render::Item::isValidID(_spaceRenderItemID)) {
|
||||||
|
_spaceRenderItemID = scene->allocateID();
|
||||||
|
auto renderItem = std::make_shared<GameWorkloadRenderItem>();
|
||||||
|
renderItem->editBound().setBox(glm::vec3(-16000.0f), 32000.0f);
|
||||||
|
renderItem->setAllProxies(proxies);
|
||||||
|
transaction.resetItem(_spaceRenderItemID, std::make_shared<GameWorkloadRenderItem::Payload>(renderItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction.updateItem<GameWorkloadRenderItem>(_spaceRenderItemID, [visible, showProxies, proxies, freezeViews, showViews, views](GameWorkloadRenderItem& item) {
|
||||||
|
item.setVisible(visible);
|
||||||
|
item.showProxies(showProxies);
|
||||||
|
item.setAllProxies(proxies);
|
||||||
|
item.showViews(showViews);
|
||||||
|
item.setAllViews(views);
|
||||||
|
});
|
||||||
|
|
||||||
|
scene->enqueueTransaction(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
template <> const ItemKey payloadGetKey(const GameWorkloadRenderItem::Pointer& payload) {
|
||||||
|
return payload->getKey();
|
||||||
|
}
|
||||||
|
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload) {
|
||||||
|
if (payload) {
|
||||||
|
return payload->getBound();
|
||||||
|
}
|
||||||
|
return Item::Bound();
|
||||||
|
}
|
||||||
|
template <> void payloadRender(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args) {
|
||||||
|
if (payload) {
|
||||||
|
payload->render(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <> const ShapeKey shapeGetShapeKey(const GameWorkloadRenderItem::Pointer& payload) {
|
||||||
|
return ShapeKey::Builder::ownPipeline();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
render::ItemKey GameWorkloadRenderItem::getKey() const {
|
||||||
|
return _key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkloadRenderItem::setVisible(bool isVisible) {
|
||||||
|
if (isVisible) {
|
||||||
|
_key = render::ItemKey::Builder(_key).withVisible();
|
||||||
|
} else {
|
||||||
|
_key = render::ItemKey::Builder(_key).withInvisible();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkloadRenderItem::showProxies(bool show) {
|
||||||
|
_showProxies = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkloadRenderItem::showViews(bool show) {
|
||||||
|
_showViews = show;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GameWorkloadRenderItem::setAllProxies(const workload::Proxy::Vector& proxies) {
|
||||||
|
_myOwnProxies = proxies;
|
||||||
|
static const uint32_t sizeOfProxy = sizeof(workload::Proxy);
|
||||||
|
if (!_allProxiesBuffer) {
|
||||||
|
_allProxiesBuffer = std::make_shared<gpu::Buffer>(sizeOfProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
_allProxiesBuffer->setData(proxies.size() * sizeOfProxy, (const gpu::Byte*) proxies.data());
|
||||||
|
_numAllProxies = (uint32_t) proxies.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkloadRenderItem::setAllViews(const workload::Views& views) {
|
||||||
|
_myOwnViews = views;
|
||||||
|
static const uint32_t sizeOfView = sizeof(workload::View);
|
||||||
|
if (!_allViewsBuffer) {
|
||||||
|
_allViewsBuffer = std::make_shared<gpu::Buffer>(sizeOfView);
|
||||||
|
}
|
||||||
|
|
||||||
|
_allViewsBuffer->setData(views.size() * sizeOfView, (const gpu::Byte*) views.data());
|
||||||
|
_numAllViews = (uint32_t)views.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpu::PipelinePointer GameWorkloadRenderItem::getProxiesPipeline() {
|
||||||
|
if (!_drawAllProxiesPipeline) {
|
||||||
|
auto vs = drawWorkloadProxy_vert::getShader();
|
||||||
|
auto ps = drawWorkloadProxy_frag::getShader();
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("workloadProxiesBuffer", 0));
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
auto state = std::make_shared<gpu::State>();
|
||||||
|
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
/* 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);*/
|
||||||
|
|
||||||
|
PrepareStencil::testMaskDrawShape(*state);
|
||||||
|
state->setCullMode(gpu::State::CULL_NONE);
|
||||||
|
_drawAllProxiesPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
return _drawAllProxiesPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const gpu::PipelinePointer GameWorkloadRenderItem::getViewsPipeline() {
|
||||||
|
if (!_drawAllViewsPipeline) {
|
||||||
|
auto vs = drawWorkloadView_vert::getShader();
|
||||||
|
auto ps = drawWorkloadView_frag::getShader();
|
||||||
|
gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps);
|
||||||
|
|
||||||
|
gpu::Shader::BindingSet slotBindings;
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("workloadViewsBuffer", 1));
|
||||||
|
slotBindings.insert(gpu::Shader::Binding("drawMeshBuffer", 0));
|
||||||
|
gpu::Shader::makeProgram(*program, slotBindings);
|
||||||
|
|
||||||
|
auto state = std::make_shared<gpu::State>();
|
||||||
|
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||||
|
/* 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);*/
|
||||||
|
|
||||||
|
PrepareStencil::testMaskDrawShape(*state);
|
||||||
|
state->setCullMode(gpu::State::CULL_NONE);
|
||||||
|
_drawAllViewsPipeline = gpu::Pipeline::create(program, state);
|
||||||
|
}
|
||||||
|
return _drawAllViewsPipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gpu::BufferPointer GameWorkloadRenderItem::getDrawViewBuffer() {
|
||||||
|
if (!_drawViewBuffer) {
|
||||||
|
int numSegments = 64;
|
||||||
|
float angleStep = (float)M_PI * 2.0f / (float)numSegments;
|
||||||
|
|
||||||
|
struct Vert {
|
||||||
|
glm::vec4 p;
|
||||||
|
};
|
||||||
|
std::vector<Vert> verts(numSegments + 1);
|
||||||
|
for (int i = 0; i < numSegments; i++) {
|
||||||
|
float angle = (float)i * angleStep;
|
||||||
|
verts[i].p.x = cos(angle);
|
||||||
|
verts[i].p.y = sin(angle);
|
||||||
|
verts[i].p.z = angle;
|
||||||
|
verts[i].p.w = 1.0f;
|
||||||
|
}
|
||||||
|
verts[numSegments] = verts[0];
|
||||||
|
verts[numSegments].p.w = 0.0f;
|
||||||
|
|
||||||
|
_drawViewBuffer = std::make_shared<gpu::Buffer>(verts.size() * sizeof(Vert), (const gpu::Byte*) verts.data());
|
||||||
|
_numDrawViewVerts = numSegments + 1;
|
||||||
|
}
|
||||||
|
return _drawViewBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameWorkloadRenderItem::render(RenderArgs* args) {
|
||||||
|
gpu::Batch& batch = *(args->_batch);
|
||||||
|
|
||||||
|
batch.setModelTransform(Transform());
|
||||||
|
|
||||||
|
batch.setResourceBuffer(0, _allProxiesBuffer);
|
||||||
|
batch.setResourceBuffer(1, _allViewsBuffer);
|
||||||
|
|
||||||
|
// Show Proxies
|
||||||
|
if (_showProxies) {
|
||||||
|
batch.setPipeline(getProxiesPipeline());
|
||||||
|
|
||||||
|
static const int NUM_VERTICES_PER_PROXY = 3;
|
||||||
|
batch.draw(gpu::TRIANGLES, NUM_VERTICES_PER_PROXY * _numAllProxies, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show Views
|
||||||
|
if (_showViews) {
|
||||||
|
batch.setPipeline(getViewsPipeline());
|
||||||
|
|
||||||
|
batch.setUniformBuffer(0, getDrawViewBuffer());
|
||||||
|
static const int NUM_VERTICES_PER_DRAWVIEWVERT = 2;
|
||||||
|
static const int NUM_REGIONS = 3;
|
||||||
|
batch.draw(gpu::TRIANGLE_STRIP, NUM_REGIONS * NUM_VERTICES_PER_DRAWVIEWVERT * _numDrawViewVerts * _numAllViews, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.setResourceBuffer(0, nullptr);
|
||||||
|
batch.setResourceBuffer(1, nullptr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
104
interface/src/workload/GameWorkloadRenderer.h
Normal file
104
interface/src/workload/GameWorkloadRenderer.h
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
//
|
||||||
|
// 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_GameWorkloadRenderer_h
|
||||||
|
#define hifi_GameWorkloadRenderer_h
|
||||||
|
|
||||||
|
#include "GameWorkload.h"
|
||||||
|
|
||||||
|
class GameSpaceToRenderConfig : public workload::Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool freezeViews MEMBER freezeViews NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool showProxies MEMBER showProxies NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool showViews MEMBER showViews NOTIFY dirty)
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool freezeViews{ false };
|
||||||
|
bool showProxies{ false };
|
||||||
|
bool showViews{ false };
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
class GameSpaceToRender {
|
||||||
|
public:
|
||||||
|
using Config = GameSpaceToRenderConfig;
|
||||||
|
using Outputs = render::Transaction;
|
||||||
|
using JobModel = workload::Job::ModelO<GameSpaceToRender, Outputs, Config>;
|
||||||
|
|
||||||
|
GameSpaceToRender() {}
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const workload::WorkloadContextPointer& renderContext, Outputs& outputs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
render::ItemID _spaceRenderItemID{ render::Item::INVALID_ITEM_ID };
|
||||||
|
bool _freezeViews{ false };
|
||||||
|
bool _showAllProxies{ false };
|
||||||
|
bool _showAllViews{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class GameWorkloadRenderItem {
|
||||||
|
public:
|
||||||
|
using Payload = render::Payload<GameWorkloadRenderItem>;
|
||||||
|
using Pointer = Payload::DataPointer;
|
||||||
|
|
||||||
|
GameWorkloadRenderItem();
|
||||||
|
~GameWorkloadRenderItem() {}
|
||||||
|
void render(RenderArgs* args);
|
||||||
|
|
||||||
|
render::Item::Bound& editBound() { return _bound; }
|
||||||
|
const render::Item::Bound& getBound() { return _bound; }
|
||||||
|
|
||||||
|
void setVisible(bool visible);
|
||||||
|
void showProxies(bool show);
|
||||||
|
void showViews(bool show);
|
||||||
|
|
||||||
|
void setAllProxies(const workload::Proxy::Vector& proxies);
|
||||||
|
void setAllViews(const workload::Views& views);
|
||||||
|
|
||||||
|
render::ItemKey getKey() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
render::Item::Bound _bound;
|
||||||
|
|
||||||
|
workload::Proxy::Vector _myOwnProxies;
|
||||||
|
gpu::BufferPointer _allProxiesBuffer;
|
||||||
|
uint32_t _numAllProxies{ 0 };
|
||||||
|
|
||||||
|
workload::Views _myOwnViews;
|
||||||
|
gpu::BufferPointer _allViewsBuffer;
|
||||||
|
uint32_t _numAllViews{ 0 };
|
||||||
|
|
||||||
|
gpu::PipelinePointer _drawAllProxiesPipeline;
|
||||||
|
const gpu::PipelinePointer getProxiesPipeline();
|
||||||
|
|
||||||
|
gpu::PipelinePointer _drawAllViewsPipeline;
|
||||||
|
const gpu::PipelinePointer getViewsPipeline();
|
||||||
|
|
||||||
|
uint32_t _numDrawViewVerts{ 0 };
|
||||||
|
gpu::BufferPointer _drawViewBuffer;
|
||||||
|
const gpu::BufferPointer getDrawViewBuffer();
|
||||||
|
|
||||||
|
render::ItemKey _key;
|
||||||
|
bool _showProxies{ true };
|
||||||
|
bool _showViews{ true };
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace render {
|
||||||
|
template <> const ItemKey payloadGetKey(const GameWorkloadRenderItem::Pointer& payload);
|
||||||
|
template <> const Item::Bound payloadGetBound(const GameWorkloadRenderItem::Pointer& payload);
|
||||||
|
template <> void payloadRender(const GameWorkloadRenderItem::Pointer& payload, RenderArgs* args);
|
||||||
|
template <> const ShapeKey shapeGetShapeKey(const GameWorkloadRenderItem::Pointer& payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
33
interface/src/workload/PhysicsBoundary.cpp
Normal file
33
interface/src/workload/PhysicsBoundary.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// PhysicsBoundary.h
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.04.05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "PhysicsBoundary.h"
|
||||||
|
|
||||||
|
#include <PhysicsLogging.h>
|
||||||
|
#include <workload/Space.h>
|
||||||
|
|
||||||
|
#include "workload/GameWorkload.h"
|
||||||
|
|
||||||
|
void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const Inputs& inputs) {
|
||||||
|
auto space = context->_space;
|
||||||
|
if (!space) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GameWorkloadContext* gameContext = static_cast<GameWorkloadContext*>(context.get());
|
||||||
|
PhysicalEntitySimulationPointer simulation = gameContext->_simulation;
|
||||||
|
const auto& regionChanges = inputs.get0();
|
||||||
|
for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) {
|
||||||
|
const workload::Space::Change& change = regionChanges[i];
|
||||||
|
auto entity = space->getOwner(change.proxyId).get<EntityItemPointer>();
|
||||||
|
if (entity) {
|
||||||
|
simulation->changeEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
interface/src/workload/PhysicsBoundary.h
Normal file
31
interface/src/workload/PhysicsBoundary.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
//
|
||||||
|
// PhysicsBoundary.h
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.04.05
|
||||||
|
// 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_PhysicsGatekeeper_h
|
||||||
|
#define hifi_PhysicsGatekeeper_h
|
||||||
|
|
||||||
|
#include <EntityItem.h>
|
||||||
|
#include <workload/Engine.h>
|
||||||
|
#include <workload/RegionTracker.h>
|
||||||
|
|
||||||
|
#include "PhysicalEntitySimulation.h"
|
||||||
|
|
||||||
|
class PhysicsBoundary {
|
||||||
|
public:
|
||||||
|
using Config = workload::Job::Config;
|
||||||
|
using Inputs = workload::RegionTracker::Outputs;
|
||||||
|
using Outputs = bool;
|
||||||
|
using JobModel = workload::Job::ModelI<PhysicsBoundary, Inputs, Config>; // this doesn't work
|
||||||
|
|
||||||
|
PhysicsBoundary() {}
|
||||||
|
void configure(const Config& config) { }
|
||||||
|
void run(const workload::WorkloadContextPointer& context, const Inputs& inputs);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_PhysicsGatekeeper_h
|
|
@ -14,6 +14,7 @@ include_hifi_library_headers(audio)
|
||||||
include_hifi_library_headers(entities)
|
include_hifi_library_headers(entities)
|
||||||
include_hifi_library_headers(octree)
|
include_hifi_library_headers(octree)
|
||||||
include_hifi_library_headers(task)
|
include_hifi_library_headers(task)
|
||||||
|
include_hifi_library_headers(workload)
|
||||||
include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h
|
include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h
|
||||||
|
|
||||||
target_bullet()
|
target_bullet()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
set(TARGET_NAME entities-renderer)
|
set(TARGET_NAME entities-renderer)
|
||||||
AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils)
|
AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils)
|
||||||
setup_hifi_library(Network Script)
|
setup_hifi_library(Network Script)
|
||||||
link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers)
|
link_hifi_libraries(shared workload gpu procedural graphics model-networking script-engine render render-utils image qml ui pointers)
|
||||||
include_hifi_library_headers(networking)
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(gl)
|
include_hifi_library_headers(gl)
|
||||||
include_hifi_library_headers(ktx)
|
include_hifi_library_headers(ktx)
|
||||||
|
|
|
@ -206,6 +206,7 @@ void EntityTreeRenderer::clear() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all entities from the scene
|
// remove all entities from the scene
|
||||||
|
_space->clear();
|
||||||
auto scene = _viewState->getMain3DScene();
|
auto scene = _viewState->getMain3DScene();
|
||||||
if (scene) {
|
if (scene) {
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
|
@ -290,6 +291,16 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
if (!entity->isParentPathComplete()) {
|
if (!entity->isParentPathComplete()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (entity->getSpaceIndex() == -1) {
|
||||||
|
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||||
|
auto spaceIndex = _space->allocateID();
|
||||||
|
workload::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius());
|
||||||
|
workload::Transaction transaction;
|
||||||
|
transaction.reset(spaceIndex, sphere, workload::Owner(entity));
|
||||||
|
_space->enqueueTransaction(transaction);
|
||||||
|
entity->setSpaceIndex(spaceIndex);
|
||||||
|
connect(entity.get(), &EntityItem::spaceUpdate, this, &EntityTreeRenderer::handleSpaceUpdate, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
auto entityID = entity->getEntityItemID();
|
auto entityID = entity->getEntityItemID();
|
||||||
processedIds.insert(entityID);
|
processedIds.insert(entityID);
|
||||||
|
@ -299,7 +310,6 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!processedIds.empty()) {
|
if (!processedIds.empty()) {
|
||||||
for (const auto& processedId : processedIds) {
|
for (const auto& processedId : processedIds) {
|
||||||
_entitiesToAdd.erase(processedId);
|
_entitiesToAdd.erase(processedId);
|
||||||
|
@ -419,10 +429,12 @@ void EntityTreeRenderer::update(bool simulate) {
|
||||||
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
|
||||||
|
|
||||||
// here we update _currentFrame and _lastAnimated and sync with the server properties.
|
// here we update _currentFrame and _lastAnimated and sync with the server properties.
|
||||||
tree->update(simulate);
|
|
||||||
|
|
||||||
// Update the rendereable entities as needed
|
|
||||||
{
|
{
|
||||||
|
PerformanceTimer perfTimer("tree::update");
|
||||||
|
tree->update(simulate);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // Update the rendereable entities as needed
|
||||||
PROFILE_RANGE(simulation_physics, "Scene");
|
PROFILE_RANGE(simulation_physics, "Scene");
|
||||||
PerformanceTimer sceneTimer("scene");
|
PerformanceTimer sceneTimer("scene");
|
||||||
auto scene = _viewState->getMain3DScene();
|
auto scene = _viewState->getMain3DScene();
|
||||||
|
@ -434,6 +446,24 @@ void EntityTreeRenderer::update(bool simulate) {
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
PerformanceTimer perfTimer("workload::transaction");
|
||||||
|
workload::Transaction spaceTransaction;
|
||||||
|
{ // update proxies in the workload::Space
|
||||||
|
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||||
|
spaceTransaction.update(_spaceUpdates);
|
||||||
|
_spaceUpdates.clear();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::vector<int32_t> staleProxies;
|
||||||
|
tree->swapStaleProxies(staleProxies);
|
||||||
|
spaceTransaction.remove(staleProxies);
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||||
|
_space->enqueueTransaction(spaceTransaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (simulate) {
|
if (simulate) {
|
||||||
// Handle enter/leave entity logic
|
// Handle enter/leave entity logic
|
||||||
|
@ -450,6 +480,11 @@ void EntityTreeRenderer::update(bool simulate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityTreeRenderer::handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUpdate) {
|
||||||
|
std::unique_lock<std::mutex> lock(_spaceLock);
|
||||||
|
_spaceUpdates.emplace_back(proxyUpdate.first, proxyUpdate.second);
|
||||||
|
}
|
||||||
|
|
||||||
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {
|
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {
|
||||||
bool didUpdate = false;
|
bool didUpdate = false;
|
||||||
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
|
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <TextureCache.h>
|
#include <TextureCache.h>
|
||||||
#include <OctreeProcessor.h>
|
#include <OctreeProcessor.h>
|
||||||
#include <render/Forward.h>
|
#include <render/Forward.h>
|
||||||
|
#include <workload/Space.h>
|
||||||
|
|
||||||
class AbstractScriptingServicesInterface;
|
class AbstractScriptingServicesInterface;
|
||||||
class AbstractViewStateInterface;
|
class AbstractViewStateInterface;
|
||||||
|
@ -116,6 +117,9 @@ public:
|
||||||
EntityItemPointer getEntity(const EntityItemID& id);
|
EntityItemPointer getEntity(const EntityItemID& id);
|
||||||
void onEntityChanged(const EntityItemID& id);
|
void onEntityChanged(const EntityItemID& id);
|
||||||
|
|
||||||
|
// Access the workload Space
|
||||||
|
workload::SpacePointer getWorkloadSpace() const { return _space; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void enterEntity(const EntityItemID& entityItemID);
|
void enterEntity(const EntityItemID& entityItemID);
|
||||||
void leaveEntity(const EntityItemID& entityItemID);
|
void leaveEntity(const EntityItemID& entityItemID);
|
||||||
|
@ -135,6 +139,8 @@ public slots:
|
||||||
EntityRendererPointer renderableForEntityId(const EntityItemID& id) const;
|
EntityRendererPointer renderableForEntityId(const EntityItemID& id) const;
|
||||||
render::ItemID renderableIdForEntityId(const EntityItemID& id) const;
|
render::ItemID renderableIdForEntityId(const EntityItemID& id) const;
|
||||||
|
|
||||||
|
void handleSpaceUpdate(std::pair<int32_t, glm::vec4> proxyUpdate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual OctreePointer createTree() override {
|
virtual OctreePointer createTree() override {
|
||||||
EntityTreePointer newTree = EntityTreePointer(new EntityTree(true));
|
EntityTreePointer newTree = EntityTreePointer(new EntityTree(true));
|
||||||
|
@ -255,6 +261,10 @@ private:
|
||||||
static int _entitiesScriptEngineCount;
|
static int _entitiesScriptEngineCount;
|
||||||
static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc;
|
static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc;
|
||||||
static std::function<bool()> _entitiesShouldFadeFunction;
|
static std::function<bool()> _entitiesShouldFadeFunction;
|
||||||
|
|
||||||
|
mutable std::mutex _spaceLock;
|
||||||
|
workload::SpacePointer _space{ new workload::Space() };
|
||||||
|
workload::Transaction::Updates _spaceUpdates;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ bool DeleteEntityOperator::preRecursion(const OctreeElementPointer& element) {
|
||||||
EntityItemPointer theEntity = details.entity;
|
EntityItemPointer theEntity = details.entity;
|
||||||
bool entityDeleted = entityTreeElement->removeEntityItem(theEntity, true); // remove it from the element
|
bool entityDeleted = entityTreeElement->removeEntityItem(theEntity, true); // remove it from the element
|
||||||
assert(entityDeleted);
|
assert(entityDeleted);
|
||||||
(void)entityDeleted; // quite warning
|
(void)entityDeleted; // quiet warning about unused variable
|
||||||
_tree->clearEntityMapEntry(details.entity->getEntityItemID());
|
_tree->clearEntityMapEntry(details.entity->getEntityItemID());
|
||||||
_foundCount++;
|
_foundCount++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,6 +367,10 @@ int EntityItem::expectedBytes() {
|
||||||
return MINIMUM_HEADER_BYTES;
|
return MINIMUM_HEADER_BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint8_t PENDING_STATE_NOTHING = 0;
|
||||||
|
const uint8_t PENDING_STATE_TAKE = 1;
|
||||||
|
const uint8_t PENDING_STATE_RELEASE = 2;
|
||||||
|
|
||||||
// clients use this method to unpack FULL updates from entity-server
|
// clients use this method to unpack FULL updates from entity-server
|
||||||
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args) {
|
||||||
setSourceUUID(args.sourceUUID);
|
setSourceUUID(args.sourceUUID);
|
||||||
|
@ -678,7 +682,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
// setters to ignore what the server says.
|
// setters to ignore what the server says.
|
||||||
filterRejection = newSimOwner.getID().isNull();
|
filterRejection = newSimOwner.getID().isNull();
|
||||||
if (weOwnSimulation) {
|
if (weOwnSimulation) {
|
||||||
if (newSimOwner.getID().isNull() && !_simulationOwner.pendingRelease(lastEditedFromBufferAdjusted)) {
|
if (newSimOwner.getID().isNull() && !pendingRelease(lastEditedFromBufferAdjusted)) {
|
||||||
// entity-server is trying to clear our ownership (probably at our own request)
|
// entity-server is trying to clear our ownership (probably at our own request)
|
||||||
// but we actually want to own it, therefore we ignore this clear event
|
// but we actually want to own it, therefore we ignore this clear event
|
||||||
// and pretend that we own it (e.g. we assume we'll receive ownership soon)
|
// and pretend that we own it (e.g. we assume we'll receive ownership soon)
|
||||||
|
@ -693,32 +697,53 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
|
||||||
// recompute weOwnSimulation for later
|
// recompute weOwnSimulation for later
|
||||||
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
||||||
}
|
}
|
||||||
} else if (_simulationOwner.pendingTake(now - maxPingRoundTrip)) {
|
} else if (_pendingOwnershipState == PENDING_STATE_TAKE) {
|
||||||
// we sent a bid already but maybe before this packet was sent from the server
|
// we're waiting to receive acceptance of a bid
|
||||||
weOwnSimulation = true;
|
// this ownership data either satisifies our bid or does not
|
||||||
|
bool bidIsSatisfied = newSimOwner.getID() == myNodeID &&
|
||||||
|
(newSimOwner.getPriority() == _pendingOwnershipPriority ||
|
||||||
|
(_pendingOwnershipPriority == VOLUNTEER_SIMULATION_PRIORITY &&
|
||||||
|
newSimOwner.getPriority() == RECRUIT_SIMULATION_PRIORITY));
|
||||||
|
|
||||||
if (newSimOwner.getID().isNull()) {
|
if (newSimOwner.getID().isNull()) {
|
||||||
// the entity-server is trying to clear someone else's ownership
|
// the entity-server is clearing someone else's ownership
|
||||||
if (!_simulationOwner.isNull()) {
|
if (!_simulationOwner.isNull()) {
|
||||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
_simulationOwner.clearCurrentOwner();
|
_simulationOwner.clearCurrentOwner();
|
||||||
}
|
}
|
||||||
} else if (newSimOwner.getID() == myNodeID) {
|
} else {
|
||||||
// the entity-server is awarding us ownership which is what we want
|
if (newSimOwner.getID() != _simulationOwner.getID()) {
|
||||||
_simulationOwner.set(newSimOwner);
|
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||||
|
}
|
||||||
|
if (_simulationOwner.set(newSimOwner)) {
|
||||||
|
// the entity-server changed ownership
|
||||||
|
somethingChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bidIsSatisfied || (somethingChanged && _pendingOwnershipTimestamp < now - maxPingRoundTrip)) {
|
||||||
|
// the bid has been satisfied, or it has been invalidated by data sent AFTER the bid should have been received
|
||||||
|
// in either case: accept our fate and clear pending state
|
||||||
|
_pendingOwnershipState = PENDING_STATE_NOTHING;
|
||||||
|
_pendingOwnershipPriority = 0;
|
||||||
|
}
|
||||||
|
weOwnSimulation = bidIsSatisfied || (_simulationOwner.getID() == myNodeID);
|
||||||
|
} else {
|
||||||
|
// we are not waiting to take ownership
|
||||||
|
if (newSimOwner.getID() != _simulationOwner.getID()) {
|
||||||
|
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
||||||
|
}
|
||||||
|
if (_simulationOwner.set(newSimOwner)) {
|
||||||
|
// the entity-server changed ownership...
|
||||||
|
somethingChanged = true;
|
||||||
|
if (newSimOwner.getID() == myNodeID) {
|
||||||
|
// we have recieved ownership
|
||||||
|
weOwnSimulation = true;
|
||||||
|
// accept our fate and clear pendingState (just in case)
|
||||||
|
_pendingOwnershipState = PENDING_STATE_NOTHING;
|
||||||
|
_pendingOwnershipPriority = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (newSimOwner.matchesValidID(myNodeID) && !_simulationOwner.pendingTake(now)) {
|
|
||||||
// entity-server tells us that we have simulation ownership while we never requested this for this EntityItem,
|
|
||||||
// this could happen when the user reloads the cache and entity tree.
|
|
||||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
|
||||||
somethingChanged = true;
|
|
||||||
_simulationOwner.clearCurrentOwner();
|
|
||||||
weOwnSimulation = false;
|
|
||||||
} else if (_simulationOwner.set(newSimOwner)) {
|
|
||||||
markDirtyFlags(Simulation::DIRTY_SIMULATOR_ID);
|
|
||||||
somethingChanged = true;
|
|
||||||
// recompute weOwnSimulation for later
|
|
||||||
weOwnSimulation = _simulationOwner.matchesValidID(myNodeID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1333,18 +1358,39 @@ void EntityItem::getAllTerseUpdateProperties(EntityItemProperties& properties) c
|
||||||
properties._accelerationChanged = true;
|
properties._accelerationChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::flagForOwnershipBid(uint8_t priority) {
|
void EntityItem::setScriptSimulationPriority(uint8_t priority) {
|
||||||
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
uint8_t newPriority = stillHasGrabActions() ? glm::max(priority, SCRIPT_GRAB_SIMULATION_PRIORITY) : priority;
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
if (newPriority != _scriptSimulationPriority) {
|
||||||
if (_simulationOwner.matchesValidID(nodeList->getSessionUUID())) {
|
// set the dirty flag to trigger a bid or ownership update
|
||||||
// we already own it
|
markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY);
|
||||||
_simulationOwner.promotePriority(priority);
|
_scriptSimulationPriority = newPriority;
|
||||||
} else {
|
|
||||||
// we don't own it yet
|
|
||||||
_simulationOwner.setPendingPriority(priority, usecTimestampNow());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::clearScriptSimulationPriority() {
|
||||||
|
// DO NOT markDirtyFlags(Simulation::DIRTY_SIMULATION_OWNERSHIP_PRIORITY) here, because this
|
||||||
|
// is only ever called from the code that actually handles the dirty flags, and it knows best.
|
||||||
|
_scriptSimulationPriority = stillHasGrabActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityItem::setPendingOwnershipPriority(uint8_t priority) {
|
||||||
|
_pendingOwnershipTimestamp = usecTimestampNow();
|
||||||
|
_pendingOwnershipPriority = priority;
|
||||||
|
_pendingOwnershipState = (_pendingOwnershipPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::pendingRelease(uint64_t timestamp) const {
|
||||||
|
return _pendingOwnershipPriority == 0 &&
|
||||||
|
_pendingOwnershipState == PENDING_STATE_RELEASE &&
|
||||||
|
_pendingOwnershipTimestamp >= timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntityItem::stillWaitingToTakeOwnership(uint64_t timestamp) const {
|
||||||
|
return _pendingOwnershipPriority > 0 &&
|
||||||
|
_pendingOwnershipState == PENDING_STATE_TAKE &&
|
||||||
|
_pendingOwnershipTimestamp >= timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
||||||
bool somethingChanged = false;
|
bool somethingChanged = false;
|
||||||
|
|
||||||
|
@ -1977,10 +2023,6 @@ void EntityItem::clearSimulationOwnership() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::setPendingOwnershipPriority(uint8_t priority, const quint64& timestamp) {
|
|
||||||
_simulationOwner.setPendingPriority(priority, timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString EntityItem::actionsToDebugString() {
|
QString EntityItem::actionsToDebugString() {
|
||||||
QString result;
|
QString result;
|
||||||
QVector<QByteArray> serializedActions;
|
QVector<QByteArray> serializedActions;
|
||||||
|
@ -2076,6 +2118,7 @@ bool EntityItem::updateAction(EntitySimulationPointer simulation, const QUuid& a
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& actionID) {
|
bool EntityItem::removeAction(EntitySimulationPointer simulation, const QUuid& actionID) {
|
||||||
|
// TODO: some action
|
||||||
bool success = false;
|
bool success = false;
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
checkWaitingToRemove(simulation);
|
checkWaitingToRemove(simulation);
|
||||||
|
@ -2403,12 +2446,17 @@ void EntityItem::locationChanged(bool tellPhysics) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
|
SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
|
||||||
|
std::pair<int32_t, glm::vec4> data(_spaceIndex, glm::vec4(getWorldPosition(), _boundingRadius));
|
||||||
|
emit spaceUpdate(data);
|
||||||
somethingChangedNotification();
|
somethingChangedNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityItem::dimensionsChanged() {
|
void EntityItem::dimensionsChanged() {
|
||||||
requiresRecalcBoxes();
|
requiresRecalcBoxes();
|
||||||
SpatiallyNestable::dimensionsChanged(); // Do what you have to do
|
SpatiallyNestable::dimensionsChanged(); // Do what you have to do
|
||||||
|
_boundingRadius = 0.5f * glm::length(getScaledDimensions());
|
||||||
|
std::pair<int32_t, glm::vec4> data(_spaceIndex, glm::vec4(getWorldPosition(), _boundingRadius));
|
||||||
|
emit spaceUpdate(data);
|
||||||
somethingChangedNotification();
|
somethingChangedNotification();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3012,6 +3060,11 @@ void EntityItem::retrieveMarketplacePublicKey() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::setSpaceIndex(int32_t index) {
|
||||||
|
assert(_spaceIndex == -1);
|
||||||
|
_spaceIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
void EntityItem::preDelete() {
|
void EntityItem::preDelete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -311,14 +311,21 @@ public:
|
||||||
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
const SimulationOwner& getSimulationOwner() const { return _simulationOwner; }
|
||||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||||
void setSimulationOwner(const SimulationOwner& owner);
|
void setSimulationOwner(const SimulationOwner& owner);
|
||||||
void promoteSimulationPriority(uint8_t priority);
|
|
||||||
|
|
||||||
uint8_t getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
uint8_t getSimulationPriority() const { return _simulationOwner.getPriority(); }
|
||||||
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
|
QUuid getSimulatorID() const { return _simulationOwner.getID(); }
|
||||||
void clearSimulationOwnership();
|
void clearSimulationOwnership();
|
||||||
void setPendingOwnershipPriority(uint8_t priority, const quint64& timestamp);
|
|
||||||
uint8_t getPendingOwnershipPriority() const { return _simulationOwner.getPendingPriority(); }
|
// TODO: move this "ScriptSimulationPriority" and "PendingOwnership" stuff into EntityMotionState
|
||||||
void rememberHasSimulationOwnershipBid() const;
|
// but first would need to do some other cleanup. In the meantime these live here as "scratch space"
|
||||||
|
// to allow libs that don't know about each other to communicate.
|
||||||
|
void setScriptSimulationPriority(uint8_t priority);
|
||||||
|
void clearScriptSimulationPriority();
|
||||||
|
uint8_t getScriptSimulationPriority() const { return _scriptSimulationPriority; }
|
||||||
|
void setPendingOwnershipPriority(uint8_t priority);
|
||||||
|
uint8_t getPendingOwnershipPriority() const { return _pendingOwnershipPriority; }
|
||||||
|
bool pendingRelease(uint64_t timestamp) const;
|
||||||
|
bool stillWaitingToTakeOwnership(uint64_t timestamp) const;
|
||||||
|
|
||||||
// Certifiable Properties
|
// Certifiable Properties
|
||||||
QString getItemName() const;
|
QString getItemName() const;
|
||||||
|
@ -411,7 +418,6 @@ public:
|
||||||
|
|
||||||
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
void getAllTerseUpdateProperties(EntityItemProperties& properties) const;
|
||||||
|
|
||||||
void flagForOwnershipBid(uint8_t priority);
|
|
||||||
void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; }
|
void flagForMotionStateChange() { _flags |= Simulation::DIRTY_MOTION_TYPE; }
|
||||||
|
|
||||||
QString actionsToDebugString();
|
QString actionsToDebugString();
|
||||||
|
@ -500,6 +506,10 @@ public:
|
||||||
void setCauterized(bool value) { _cauterized = value; }
|
void setCauterized(bool value) { _cauterized = value; }
|
||||||
bool getCauterized() const { return _cauterized; }
|
bool getCauterized() const { return _cauterized; }
|
||||||
|
|
||||||
|
float getBoundingRadius() const { return _boundingRadius; }
|
||||||
|
void setSpaceIndex(int32_t index);
|
||||||
|
int32_t getSpaceIndex() const { return _spaceIndex; }
|
||||||
|
|
||||||
virtual void preDelete();
|
virtual void preDelete();
|
||||||
virtual void postParentFixup() {}
|
virtual void postParentFixup() {}
|
||||||
|
|
||||||
|
@ -517,6 +527,7 @@ public:
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void requestRenderUpdate();
|
void requestRenderUpdate();
|
||||||
|
void spaceUpdate(std::pair<int32_t, glm::vec4> data);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
||||||
|
@ -668,6 +679,17 @@ protected:
|
||||||
quint64 _lastUpdatedQueryAACubeTimestamp { 0 };
|
quint64 _lastUpdatedQueryAACubeTimestamp { 0 };
|
||||||
uint64_t _simulationOwnershipExpiry { 0 };
|
uint64_t _simulationOwnershipExpiry { 0 };
|
||||||
|
|
||||||
|
float _boundingRadius { 0.0f };
|
||||||
|
int32_t _spaceIndex { -1 }; // index to proxy in workload::Space
|
||||||
|
|
||||||
|
// TODO: move this "scriptSimulationPriority" and "pendingOwnership" stuff into EntityMotionState
|
||||||
|
// but first would need to do some other cleanup. In the meantime these live here as "scratch space"
|
||||||
|
// to allow libs that don't know about each other to communicate.
|
||||||
|
uint64_t _pendingOwnershipTimestamp { 0 }; // timestamp of last owenership change request
|
||||||
|
uint8_t _pendingOwnershipPriority { 0 }; // priority of last ownership change request
|
||||||
|
uint8_t _pendingOwnershipState { 0 }; // TAKE or RELEASE
|
||||||
|
uint8_t _scriptSimulationPriority { 0 }; // target priority based on script operations
|
||||||
|
|
||||||
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
|
bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera
|
||||||
|
|
||||||
bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE };
|
bool _cloneable { ENTITY_ITEM_DEFAULT_CLONEABLE };
|
||||||
|
|
|
@ -339,7 +339,7 @@ public:
|
||||||
void clearSimulationOwner();
|
void clearSimulationOwner();
|
||||||
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
void setSimulationOwner(const QUuid& id, uint8_t priority);
|
||||||
void setSimulationOwner(const QByteArray& data);
|
void setSimulationOwner(const QByteArray& data);
|
||||||
void promoteSimulationPriority(uint8_t priority) { _simulationOwner.promotePriority(priority); }
|
void setSimulationPriority(uint8_t priority) { _simulationOwner.setPriority(priority); }
|
||||||
|
|
||||||
void setActionDataDirty() { _actionDataChanged = true; }
|
void setActionDataDirty() { _actionDataChanged = true; }
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,7 @@ bool EntityScriptingInterface::addLocalEntityCopy(EntityItemProperties& properti
|
||||||
|
|
||||||
entity->setLastBroadcast(usecTimestampNow());
|
entity->setLastBroadcast(usecTimestampNow());
|
||||||
// since we're creating this object we will immediately volunteer to own its simulation
|
// since we're creating this object we will immediately volunteer to own its simulation
|
||||||
entity->flagForOwnershipBid(VOLUNTEER_SIMULATION_PRIORITY);
|
entity->setScriptSimulationPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
||||||
properties.setLastEdited(entity->getLastEdited());
|
properties.setLastEdited(entity->getLastEdited());
|
||||||
} else {
|
} else {
|
||||||
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
qCDebug(entities) << "script failed to add new Entity to local Octree";
|
||||||
|
@ -494,7 +494,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties&
|
||||||
} else {
|
} else {
|
||||||
// we make a bid for simulation ownership
|
// we make a bid for simulation ownership
|
||||||
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
properties.setSimulationOwner(myNodeID, SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||||
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
|
entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (properties.queryAACubeRelatedPropertyChanged()) {
|
if (properties.queryAACubeRelatedPropertyChanged()) {
|
||||||
|
@ -1363,7 +1363,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString,
|
||||||
}
|
}
|
||||||
action->setIsMine(true);
|
action->setIsMine(true);
|
||||||
success = entity->addAction(simulation, action);
|
success = entity->addAction(simulation, action);
|
||||||
entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||||
});
|
});
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -1379,7 +1379,7 @@ bool EntityScriptingInterface::updateAction(const QUuid& entityID, const QUuid&
|
||||||
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
return actionWorker(entityID, [&](EntitySimulationPointer simulation, EntityItemPointer entity) {
|
||||||
bool success = entity->updateAction(simulation, actionID, arguments);
|
bool success = entity->updateAction(simulation, actionID, arguments);
|
||||||
if (success) {
|
if (success) {
|
||||||
entity->flagForOwnershipBid(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
entity->setScriptSimulationPriority(SCRIPT_GRAB_SIMULATION_PRIORITY);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
});
|
});
|
||||||
|
@ -1393,7 +1393,7 @@ bool EntityScriptingInterface::deleteAction(const QUuid& entityID, const QUuid&
|
||||||
success = entity->removeAction(simulation, actionID);
|
success = entity->removeAction(simulation, actionID);
|
||||||
if (success) {
|
if (success) {
|
||||||
// reduce from grab to poke
|
// reduce from grab to poke
|
||||||
entity->flagForOwnershipBid(SCRIPT_POKE_SIMULATION_PRIORITY);
|
entity->setScriptSimulationPriority(SCRIPT_POKE_SIMULATION_PRIORITY);
|
||||||
}
|
}
|
||||||
return false; // Physics will cause a packet to be sent, so don't send from here.
|
return false; // Physics will cause a packet to be sent, so don't send from here.
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,6 +31,7 @@ void EntitySimulation::setEntityTree(EntityTreePointer tree) {
|
||||||
void EntitySimulation::updateEntities() {
|
void EntitySimulation::updateEntities() {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
uint64_t now = usecTimestampNow();
|
uint64_t now = usecTimestampNow();
|
||||||
|
PerformanceTimer perfTimer("EntitySimulation::updateEntities");
|
||||||
|
|
||||||
// these methods may accumulate entries in _entitiesToBeDeleted
|
// these methods may accumulate entries in _entitiesToBeDeleted
|
||||||
expireMortalEntities(now);
|
expireMortalEntities(now);
|
||||||
|
|
|
@ -97,6 +97,7 @@ void EntityTree::eraseAllOctreeElements(bool createNewRoot) {
|
||||||
if (_simulation) {
|
if (_simulation) {
|
||||||
_simulation->clearEntities();
|
_simulation->clearEntities();
|
||||||
}
|
}
|
||||||
|
_staleProxies.clear();
|
||||||
QHash<EntityItemID, EntityItemPointer> localMap;
|
QHash<EntityItemID, EntityItemPointer> localMap;
|
||||||
localMap.swap(_entityMap);
|
localMap.swap(_entityMap);
|
||||||
this->withWriteLock([&] {
|
this->withWriteLock([&] {
|
||||||
|
@ -276,10 +277,11 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_isDirty = true;
|
_isDirty = true;
|
||||||
emit addingEntity(entity->getEntityItemID());
|
|
||||||
|
|
||||||
// find and hook up any entities with this entity as a (previously) missing parent
|
// find and hook up any entities with this entity as a (previously) missing parent
|
||||||
fixupNeedsParentFixups();
|
fixupNeedsParentFixups();
|
||||||
|
|
||||||
|
emit addingEntity(entity->getEntityItemID());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
bool EntityTree::updateEntity(const EntityItemID& entityID, const EntityItemProperties& properties, const SharedNodePointer& senderNode) {
|
||||||
|
@ -359,21 +361,34 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
||||||
// the sender is trying to take or continue ownership
|
// the sender is trying to take or continue ownership
|
||||||
if (entity->getSimulatorID().isNull()) {
|
if (entity->getSimulatorID().isNull()) {
|
||||||
// the sender is taking ownership
|
// the sender is taking ownership
|
||||||
properties.promoteSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
if (properties.getSimulationOwner().getPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
|
||||||
|
// the entity-server always promotes VOLUNTEER to RECRUIT to avoid ownership thrash
|
||||||
|
// when dynamic objects first activate and multiple participants bid simultaneously
|
||||||
|
properties.setSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||||
|
}
|
||||||
simulationBlocked = false;
|
simulationBlocked = false;
|
||||||
} else if (entity->getSimulatorID() == senderID) {
|
} else if (entity->getSimulatorID() == senderID) {
|
||||||
// the sender is asserting ownership, maybe changing priority
|
// the sender is asserting ownership, maybe changing priority
|
||||||
simulationBlocked = false;
|
simulationBlocked = false;
|
||||||
|
// the entity-server always promotes VOLUNTEER to RECRUIT to avoid ownership thrash
|
||||||
|
// when dynamic objects first activate and multiple participants bid simultaneously
|
||||||
|
if (properties.getSimulationOwner().getPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
|
||||||
|
properties.setSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// the sender is trying to steal ownership from another simulator
|
// the sender is trying to steal ownership from another simulator
|
||||||
// so we apply the rules for ownership change:
|
// so we apply the rules for ownership change:
|
||||||
// (1) higher priority wins
|
// (1) higher priority wins
|
||||||
// (2) equal priority wins if ownership filter has expired except...
|
// (2) equal priority wins if ownership filter has expired
|
||||||
|
// (3) VOLUNTEER priority is promoted to RECRUIT
|
||||||
uint8_t oldPriority = entity->getSimulationPriority();
|
uint8_t oldPriority = entity->getSimulationPriority();
|
||||||
uint8_t newPriority = properties.getSimulationOwner().getPriority();
|
uint8_t newPriority = properties.getSimulationOwner().getPriority();
|
||||||
if (newPriority > oldPriority ||
|
if (newPriority > oldPriority ||
|
||||||
(newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) {
|
(newPriority == oldPriority && properties.getSimulationOwner().hasExpired())) {
|
||||||
simulationBlocked = false;
|
simulationBlocked = false;
|
||||||
|
if (properties.getSimulationOwner().getPriority() == VOLUNTEER_SIMULATION_PRIORITY) {
|
||||||
|
properties.setSimulationPriority(RECRUIT_SIMULATION_PRIORITY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!simulationBlocked) {
|
if (!simulationBlocked) {
|
||||||
|
@ -391,6 +406,7 @@ bool EntityTree::updateEntity(EntityItemPointer entity, const EntityItemProperti
|
||||||
}
|
}
|
||||||
if (simulationBlocked) {
|
if (simulationBlocked) {
|
||||||
// squash ownership and physics-related changes.
|
// squash ownership and physics-related changes.
|
||||||
|
// TODO? replace these eight calls with just one?
|
||||||
properties.setSimulationOwnerChanged(false);
|
properties.setSimulationOwnerChanged(false);
|
||||||
properties.setPositionChanged(false);
|
properties.setPositionChanged(false);
|
||||||
properties.setRotationChanged(false);
|
properties.setRotationChanged(false);
|
||||||
|
@ -729,6 +745,12 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
|
||||||
if (theEntity->isSimulated()) {
|
if (theEntity->isSimulated()) {
|
||||||
_simulation->prepareEntityForDelete(theEntity);
|
_simulation->prepareEntityForDelete(theEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// keep a record of valid stale spaceIndices so they can be removed from the Space
|
||||||
|
int32_t spaceIndex = theEntity->getSpaceIndex();
|
||||||
|
if (spaceIndex != -1) {
|
||||||
|
_staleProxies.push_back(spaceIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1853,6 +1875,7 @@ void EntityTree::addToNeedsParentFixupList(EntityItemPointer entity) {
|
||||||
|
|
||||||
void EntityTree::update(bool simulate) {
|
void EntityTree::update(bool simulate) {
|
||||||
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
PROFILE_RANGE(simulation_physics, "UpdateTree");
|
||||||
|
PerformanceTimer perfTimer("updateTree");
|
||||||
withWriteLock([&] {
|
withWriteLock([&] {
|
||||||
fixupNeedsParentFixups();
|
fixupNeedsParentFixups();
|
||||||
if (simulate && _simulation) {
|
if (simulate && _simulation) {
|
||||||
|
|
|
@ -274,6 +274,8 @@ public:
|
||||||
|
|
||||||
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
|
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
|
||||||
|
|
||||||
|
void swapStaleProxies(std::vector<int>& proxies) { proxies.swap(_staleProxies); }
|
||||||
|
|
||||||
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
|
void setIsServerlessMode(bool value) { _serverlessDomain = value; }
|
||||||
bool isServerlessMode() const { return _serverlessDomain; }
|
bool isServerlessMode() const { return _serverlessDomain; }
|
||||||
|
|
||||||
|
@ -408,6 +410,8 @@ private:
|
||||||
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToOverlayOperator;
|
static std::function<bool(const QUuid&, graphics::MaterialLayer, const std::string&)> _addMaterialToOverlayOperator;
|
||||||
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromOverlayOperator;
|
static std::function<bool(const QUuid&, graphics::MaterialPointer, const std::string&)> _removeMaterialFromOverlayOperator;
|
||||||
|
|
||||||
|
std::vector<int32_t> _staleProxies;
|
||||||
|
|
||||||
bool _serverlessDomain { false };
|
bool _serverlessDomain { false };
|
||||||
|
|
||||||
std::map<QString, QString> _namedPaths;
|
std::map<QString, QString> _namedPaths;
|
||||||
|
|
|
@ -53,20 +53,29 @@ void SimpleEntitySimulation::updateEntitiesInternal(uint64_t now) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
if (entity->getSimulatorID().isNull()) {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
_simpleKinematicEntities.insert(entity);
|
if (entity->getDynamic()) {
|
||||||
entity->setLastSimulated(usecTimestampNow());
|
// we don't allow dynamic objects to move without an owner so nothing to do here
|
||||||
}
|
} else if (entity->isMovingRelativeToParent()) {
|
||||||
if (!entity->getSimulatorID().isNull()) {
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.insert(entity);
|
||||||
|
entity->setLastSimulated(usecTimestampNow());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
_entitiesWithSimulationOwner.insert(entity);
|
_entitiesWithSimulationOwner.insert(entity);
|
||||||
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
||||||
} else if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
|
||||||
QMutexLocker lock(&_mutex);
|
if (entity->isMovingRelativeToParent()) {
|
||||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
|
_simpleKinematicEntities.insert(entity);
|
||||||
|
entity->setLastSimulated(usecTimestampNow());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,32 +86,50 @@ void SimpleEntitySimulation::removeEntityInternal(EntityItemPointer entity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
void SimpleEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
{
|
uint32_t flags = entity->getDirtyFlags();
|
||||||
QMutexLocker lock(&_mutex);
|
if ((flags & Simulation::DIRTY_SIMULATOR_ID) || (flags & Simulation::DIRTY_VELOCITIES)) {
|
||||||
if (entity->isMovingRelativeToParent() && !entity->getPhysicsInfo()) {
|
if (entity->getSimulatorID().isNull()) {
|
||||||
int numKinematicEntities = _simpleKinematicEntities.size();
|
QMutexLocker lock(&_mutex);
|
||||||
_simpleKinematicEntities.insert(entity);
|
_entitiesWithSimulationOwner.remove(entity);
|
||||||
if (numKinematicEntities != _simpleKinematicEntities.size()) {
|
|
||||||
entity->setLastSimulated(usecTimestampNow());
|
if (entity->getDynamic()) {
|
||||||
|
// we don't allow dynamic objects to move without an owner
|
||||||
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.erase(itr);
|
||||||
|
}
|
||||||
|
} else if (entity->isMovingRelativeToParent()) {
|
||||||
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.insert(entity);
|
||||||
|
entity->setLastSimulated(usecTimestampNow());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.erase(itr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_simpleKinematicEntities.remove(entity);
|
QMutexLocker lock(&_mutex);
|
||||||
|
_entitiesWithSimulationOwner.insert(entity);
|
||||||
|
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
||||||
|
_entitiesThatNeedSimulationOwner.remove(entity);
|
||||||
|
|
||||||
|
if (entity->isMovingRelativeToParent()) {
|
||||||
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.insert(entity);
|
||||||
|
entity->setLastSimulated(usecTimestampNow());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.erase(itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entity->getSimulatorID().isNull()) {
|
|
||||||
QMutexLocker lock(&_mutex);
|
|
||||||
_entitiesWithSimulationOwner.remove(entity);
|
|
||||||
if (entity->getDynamic() && entity->hasLocalVelocity()) {
|
|
||||||
_entitiesThatNeedSimulationOwner.insert(entity);
|
|
||||||
uint64_t expiry = entity->getLastChangedOnServer() + MAX_OWNERLESS_PERIOD;
|
|
||||||
_nextOwnerlessExpiry = glm::min(_nextOwnerlessExpiry, expiry);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QMutexLocker lock(&_mutex);
|
|
||||||
_entitiesWithSimulationOwner.insert(entity);
|
|
||||||
_nextStaleOwnershipExpiry = glm::min(_nextStaleOwnershipExpiry, entity->getSimulationOwnershipExpiry());
|
|
||||||
_entitiesThatNeedSimulationOwner.remove(entity);
|
|
||||||
}
|
|
||||||
entity->clearDirtyFlags();
|
entity->clearDirtyFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +158,12 @@ void SimpleEntitySimulation::expireStaleOwnerships(uint64_t now) {
|
||||||
uint64_t expiry = entity->getSimulationOwnershipExpiry();
|
uint64_t expiry = entity->getSimulationOwnershipExpiry();
|
||||||
if (now > expiry) {
|
if (now > expiry) {
|
||||||
itemItr = _entitiesWithSimulationOwner.erase(itemItr);
|
itemItr = _entitiesWithSimulationOwner.erase(itemItr);
|
||||||
|
if (entity->getDynamic()) {
|
||||||
|
SetOfEntities::iterator itr = _simpleKinematicEntities.find(entity);
|
||||||
|
if (itr != _simpleKinematicEntities.end()) {
|
||||||
|
_simpleKinematicEntities.erase(itr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove ownership and dirty all the tree elements that contain the it
|
// remove ownership and dirty all the tree elements that contain the it
|
||||||
entity->clearSimulationOwnership();
|
entity->clearSimulationOwnership();
|
||||||
|
|
|
@ -16,29 +16,20 @@
|
||||||
|
|
||||||
#include <NumericalConstants.h>
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
const uint8_t PENDING_STATE_NOTHING = 0;
|
|
||||||
const uint8_t PENDING_STATE_TAKE = 1;
|
|
||||||
const uint8_t PENDING_STATE_RELEASE = 2;
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
|
const int SimulationOwner::NUM_BYTES_ENCODED = NUM_BYTES_RFC4122_UUID + 1;
|
||||||
|
|
||||||
SimulationOwner::SimulationOwner() :
|
SimulationOwner::SimulationOwner() :
|
||||||
_id(),
|
_id(),
|
||||||
_expiry(0),
|
_expiry(0),
|
||||||
_pendingBidTimestamp(0),
|
_priority(0)
|
||||||
_priority(0),
|
|
||||||
_pendingBidPriority(0),
|
|
||||||
_pendingState(PENDING_STATE_NOTHING)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SimulationOwner::SimulationOwner(const QUuid& id, uint8_t priority) :
|
SimulationOwner::SimulationOwner(const QUuid& id, uint8_t priority) :
|
||||||
_id(id),
|
_id(id),
|
||||||
_expiry(0),
|
_expiry(0),
|
||||||
_pendingBidTimestamp(0),
|
_priority(priority)
|
||||||
_priority(priority),
|
|
||||||
_pendingBidPriority(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,22 +52,13 @@ bool SimulationOwner::fromByteArray(const QByteArray& data) {
|
||||||
void SimulationOwner::clear() {
|
void SimulationOwner::clear() {
|
||||||
_id = QUuid();
|
_id = QUuid();
|
||||||
_expiry = 0;
|
_expiry = 0;
|
||||||
_pendingBidTimestamp = 0;
|
|
||||||
_priority = 0;
|
_priority = 0;
|
||||||
_pendingBidPriority = 0;
|
|
||||||
_pendingState = PENDING_STATE_NOTHING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulationOwner::setPriority(uint8_t priority) {
|
void SimulationOwner::setPriority(uint8_t priority) {
|
||||||
_priority = priority;
|
_priority = priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulationOwner::promotePriority(uint8_t priority) {
|
|
||||||
if (priority > _priority) {
|
|
||||||
_priority = priority;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SimulationOwner::setID(const QUuid& id) {
|
bool SimulationOwner::setID(const QUuid& id) {
|
||||||
if (_id != id) {
|
if (_id != id) {
|
||||||
_id = id;
|
_id = id;
|
||||||
|
@ -101,25 +83,11 @@ bool SimulationOwner::set(const SimulationOwner& owner) {
|
||||||
return setID(owner._id) || oldPriority != _priority;
|
return setID(owner._id) || oldPriority != _priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SimulationOwner::setPendingPriority(uint8_t priority, uint64_t timestamp) {
|
|
||||||
_pendingBidPriority = priority;
|
|
||||||
_pendingBidTimestamp = timestamp;
|
|
||||||
_pendingState = (_pendingBidPriority == 0) ? PENDING_STATE_RELEASE : PENDING_STATE_TAKE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulationOwner::updateExpiry() {
|
void SimulationOwner::updateExpiry() {
|
||||||
const uint64_t OWNERSHIP_LOCKOUT_EXPIRY = 200 * USECS_PER_MSEC;
|
const uint64_t OWNERSHIP_LOCKOUT_EXPIRY = 200 * USECS_PER_MSEC;
|
||||||
_expiry = usecTimestampNow() + OWNERSHIP_LOCKOUT_EXPIRY;
|
_expiry = usecTimestampNow() + OWNERSHIP_LOCKOUT_EXPIRY;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SimulationOwner::pendingRelease(uint64_t timestamp) {
|
|
||||||
return _pendingBidPriority == 0 && _pendingState == PENDING_STATE_RELEASE && _pendingBidTimestamp >= timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SimulationOwner::pendingTake(uint64_t timestamp) {
|
|
||||||
return _pendingBidPriority > 0 && _pendingState == PENDING_STATE_TAKE && _pendingBidTimestamp >= timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SimulationOwner::clearCurrentOwner() {
|
void SimulationOwner::clearCurrentOwner() {
|
||||||
_id = QUuid();
|
_id = QUuid();
|
||||||
_expiry = 0;
|
_expiry = 0;
|
||||||
|
|
|
@ -19,75 +19,75 @@
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
|
|
||||||
// HighFidelity uses a distributed physics simulation where multiple "participants" simulate portions
|
// HighFidelity uses a distributed physics simulation where multiple "participants" simulate portions
|
||||||
// of the same world. When portions overlap only one participant is allowed to be the authority for any
|
// of the same domain. Even when portions overlap only one participant is allowed to be the current
|
||||||
// particular object. For a simulated entity the authoritative participant is called the simulation "owner" and
|
// authority for any particular object's physical simulation. The authoritative participant is called the
|
||||||
// their duty is to send transform/velocity updates for the entity to the central entity-server.
|
// "simulation owner" and its duty is to send "state synchronization" (transform/velocity) updates for an
|
||||||
// The entity-server relays updates to other participants who apply them as "state synchronization"
|
// entity to the entity-server. The entity-server relays updates to other participants who apply them to
|
||||||
// to their own simulation.
|
// their own simulation.
|
||||||
//
|
//
|
||||||
// Participants acquire ownership by sending a "bid" to the entity-server. The bid is a properties update:
|
// Participants acquire ownership by sending a "bid" to the entity-server. The bid is a properties update:
|
||||||
// {
|
// { "simulationOwner": { "ownerID" : sessionID, "priority" : priority },
|
||||||
// "simulationOwner": { "ownerID" : sessionID, "priority" : priority },
|
|
||||||
// transform/velocity properties
|
// transform/velocity properties
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// The entity-server is the authority as to who owns what and may reject a bid.
|
// The entity-server is the authority as to who owns what and may reject a bid. The rules for handling a
|
||||||
// The rules for handling a bid are as follows:
|
// bid are as follows:
|
||||||
//
|
//
|
||||||
// (1) A bid may be refused for special ownership restrictions, but otherwise...
|
// (1) A bid may be refused for special ownership restrictions, but otherwise...
|
||||||
//
|
//
|
||||||
// (2) A bid at higher priority is accepted
|
// (2) A bid at higher priority is accepted
|
||||||
//
|
//
|
||||||
// (3) A bid at equal priority is rejected if receieved within a grace-period (200msec)
|
// (3) A bid at equal priority is accepted unless it was received shortly after (within 200msec) of the
|
||||||
// of the last ownership transition, otherwise it is accepted
|
// last ownership change. This to avoid rapid ownership transitions should multiple participants
|
||||||
|
// bid simultaneously.
|
||||||
//
|
//
|
||||||
// (4) The current owner is the only participant allowed to clear ownership (entity-server can override).
|
// (4) The current owner is the only participant allowed to clear their ownership or adjust priority.
|
||||||
//
|
//
|
||||||
// (5) The current owner is the only participant allowed to adjust priority (entity-server can override).
|
// (5) If an owner does not update the transform or velocities of an owned entity within some period
|
||||||
//
|
|
||||||
// (6) If an owner does not update the transform or velocities of an owned entity within some period
|
|
||||||
// (5 seconds) then ownership is cleared and the entity's velocities are zeroed. This to handle
|
// (5 seconds) then ownership is cleared and the entity's velocities are zeroed. This to handle
|
||||||
// the case when an owner drops off the network.
|
// the case when an owner drops off the network.
|
||||||
//
|
//
|
||||||
// The priority of a participant's bid depends on how "interested" it is in the entity's motion. The rules
|
// The priority of a participant's bid depends on how "interested" it is in the entity's motion. The rules
|
||||||
// for bidding are as follows:
|
// for bidding are as follows:
|
||||||
//
|
//
|
||||||
// (7) A participant (almost) never assumes that a bid is accepted by the entity-server. It packs the
|
// (6) A participant (almost) never assumes its bid is accepted by the entity-server. It packs the
|
||||||
// simulation owner and priority as if they really did change but doesn't actually modify them
|
// simulation owner properties as if they really did change but doesn't actually modify them
|
||||||
// locally. Thus, if the bid packet is lost the participant will re-send after some period.
|
// locally. Instead it waits to hear back from the entity-server for bid acceptance. If the entity
|
||||||
// The participant only updates its knowledge of who owns what when it recieves an update from the
|
// remains unowned the participant will resend the bid (assuming the bid pakcet was lost). The
|
||||||
// entity-server. An exception is when the participant creates a moving entity: it assumes it starts
|
// exception is when the participant creates a moving entity: it assumes it starts off owning any
|
||||||
// off owning any moving entities it creates.
|
// moving entities it creates.
|
||||||
//
|
//
|
||||||
// (8) When an unowned entity becomes active in the physics simulation the participant will
|
// (7) When an entity becomes active in the physics simulation but is not owned the participant will
|
||||||
// start a timer and if the entity is still unowned after some period (0.5 seconds)
|
// start a timer and if it is still unowned after expiry (0.5 seconds) the participant will
|
||||||
// it will bid at priority = VOLUNTEER (=2). The entity-server never grants ownership at VOLUNTEER
|
// bid at priority = VOLUNTEER (=2). The entity-server never grants ownership at VOLUNTEER
|
||||||
// priority: when a VOLUNTEER bid is accepted the entity-server always promotes the priority to
|
// priority: when a VOLUNTEER bid is accepted the entity-server always promotes the priority to
|
||||||
// RECRUIT (VOLUNTEER + 1); this to avoid a race-condition which might rapidly transition ownership
|
// RECRUIT (=VOLUNTEER + 1); this to avoid a race-condition which might rapidly transition ownership
|
||||||
// when multiple participants (with variable ping-times to the server) bid simultaneously for a
|
// when multiple participants (with variable ping-times to the server) bid simultaneously for a
|
||||||
// recently activated entity.
|
// recently activated entity.
|
||||||
//
|
//
|
||||||
// (9) When a participant changes an entity's transform/velocity (via script) it will bid at
|
// (8) When a participant's script changes an entity's transform/velocity the participant will bid at
|
||||||
// priority = POKE (=127).
|
// priority = POKE (=127)
|
||||||
//
|
//
|
||||||
// (10) When an entity collides with MyAvatar the participant it will bid at priority = POKE.
|
// (9) When an entity collides against MyAvatar the participant will bid at priority = POKE.
|
||||||
//
|
//
|
||||||
// (11) When a participant grabs (via script) an entity it will bid at priority = GRAB (=128).
|
// (10) When a participant grabs an entity it will bid at priority = GRAB (=128). This to allow UserA
|
||||||
|
// to whack UserB with a "sword" without losing ownership, since UserB will bid at POKE. If UserB
|
||||||
|
// wants to contest for ownership they must also GRAB it.
|
||||||
//
|
//
|
||||||
// (12) When entityA, locally owned at priority = N, collides with an unowned entityB the owner will
|
// (11) When EntityA, locally owned at priority = N, collides with an unowned EntityB the owner will
|
||||||
// also bid for entityB at priority = N-1 (or VOLUNTEER, whichever is larger).
|
// also bid for EntityB at priority = N-1 (or VOLUNTEER, whichever is larger).
|
||||||
//
|
//
|
||||||
// (13) When an entity comes to rest and is deactivated in the physics simulation the owner will
|
// (12) When an entity comes to rest and is deactivated in the physics simulation the owner will send
|
||||||
// send an update to: clear their ownerhsip, set priority to zero, and set the object's
|
// an update to: clear their ownerhsip, set priority to zero, and set the entity's velocities to
|
||||||
// velocities to be zero. As per a normal bid, the owner does NOT assume that its ownership
|
// zero. As per a normal bid, the owner does NOT assume its ownership has been cleared until
|
||||||
// has been cleared until it hears from the entity-server. Thus, if the packet is lost the
|
// it hears back from the entity-server. Thus, if the packet is lost the owner will re-send after
|
||||||
// owner will re-send after some period.
|
// expiry.
|
||||||
//
|
//
|
||||||
// (14) When an entity's ownership priority drops below VOLUNTEER other participants may bid for it
|
// (13) When an entity is still active but the owner no longer wants to own it, the owner will drop its
|
||||||
// immediately at priority = VOLUNTEER.
|
// priority to YIELD (=1, below VOLUNTEER) thereby signalling to other participants to bid for it.
|
||||||
//
|
//
|
||||||
// (15) When an entity is still active but the owner no longer wants to own it, it will drop its priority
|
// (14) When an entity's ownership priority drops to YIELD (=1, below VOLUNTEER) other participants may
|
||||||
// to YIELD (=1, less than VOLUNTEER) thereby signalling to other participants to bid for it.
|
// bid for it immediately at VOLUNTEER.
|
||||||
//
|
//
|
||||||
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
const uint8_t YIELD_SIMULATION_PRIORITY = 1;
|
||||||
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
const uint8_t VOLUNTEER_SIMULATION_PRIORITY = YIELD_SIMULATION_PRIORITY + 1;
|
||||||
|
@ -120,24 +120,18 @@ public:
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void setPriority(uint8_t priority);
|
void setPriority(uint8_t priority);
|
||||||
void promotePriority(uint8_t priority);
|
|
||||||
|
|
||||||
// return true if id is changed
|
// return true if id is changed
|
||||||
bool setID(const QUuid& id);
|
bool setID(const QUuid& id);
|
||||||
bool set(const QUuid& id, uint8_t priority);
|
bool set(const QUuid& id, uint8_t priority);
|
||||||
bool set(const SimulationOwner& owner);
|
bool set(const SimulationOwner& owner);
|
||||||
void setPendingPriority(uint8_t priority, uint64_t timestamp);
|
|
||||||
|
|
||||||
bool isNull() const { return _id.isNull(); }
|
bool isNull() const { return _id.isNull(); }
|
||||||
bool matchesValidID(const QUuid& id) const { return _id == id && !_id.isNull(); }
|
bool matchesValidID(const QUuid& id) const { return _id == id && !_id.isNull(); }
|
||||||
|
|
||||||
void updateExpiry();
|
void updateExpiry();
|
||||||
|
|
||||||
bool hasExpired() const { return usecTimestampNow() > _expiry; }
|
bool hasExpired() const { return usecTimestampNow() > _expiry; }
|
||||||
|
|
||||||
uint8_t getPendingPriority() const { return _pendingBidPriority; }
|
|
||||||
bool pendingRelease(uint64_t timestamp); // return true if valid pending RELEASE
|
|
||||||
bool pendingTake(uint64_t timestamp); // return true if valid pending TAKE
|
|
||||||
void clearCurrentOwner();
|
void clearCurrentOwner();
|
||||||
|
|
||||||
bool operator>=(uint8_t priority) const { return _priority >= priority; }
|
bool operator>=(uint8_t priority) const { return _priority >= priority; }
|
||||||
|
@ -154,10 +148,7 @@ public:
|
||||||
private:
|
private:
|
||||||
QUuid _id; // owner
|
QUuid _id; // owner
|
||||||
uint64_t _expiry; // time when ownership can transition at equal priority
|
uint64_t _expiry; // time when ownership can transition at equal priority
|
||||||
uint64_t _pendingBidTimestamp; // time when pending bid was set
|
|
||||||
uint8_t _priority; // priority of current owner
|
uint8_t _priority; // priority of current owner
|
||||||
uint8_t _pendingBidPriority; // priority at which we'd like to own it
|
|
||||||
uint8_t _pendingState; // NOTHING, TAKE, or RELEASE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ int GL45Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, cons
|
||||||
auto requestedBinding = slotBindings.find(info.name);
|
auto requestedBinding = slotBindings.find(info.name);
|
||||||
if (requestedBinding != slotBindings.end()) {
|
if (requestedBinding != slotBindings.end()) {
|
||||||
info.binding = (*requestedBinding)._location;
|
info.binding = (*requestedBinding)._location;
|
||||||
glUniformBlockBinding(glprogram, info.index, info.binding);
|
glShaderStorageBlockBinding(glprogram, info.index, info.binding);
|
||||||
resourceBufferSlotMap[info.binding] = info.index;
|
resourceBufferSlotMap[info.binding] = info.index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ int GL45Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, cons
|
||||||
auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), GLBackend::isUnusedSlot);
|
auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), GLBackend::isUnusedSlot);
|
||||||
if (slotIt != resourceBufferSlotMap.end()) {
|
if (slotIt != resourceBufferSlotMap.end()) {
|
||||||
info.binding = slotIt - resourceBufferSlotMap.begin();
|
info.binding = slotIt - resourceBufferSlotMap.begin();
|
||||||
glUniformBlockBinding(glprogram, info.index, info.binding);
|
glShaderStorageBlockBinding(glprogram, info.index, info.binding);
|
||||||
} else {
|
} else {
|
||||||
// This should never happen, an active ssbo cannot find an available slot among the max available?!
|
// This should never happen, an active ssbo cannot find an available slot among the max available?!
|
||||||
info.binding = -1;
|
info.binding = -1;
|
||||||
|
|
|
@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityEdit:
|
case PacketType::EntityEdit:
|
||||||
case PacketType::EntityData:
|
case PacketType::EntityData:
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return static_cast<PacketVersion>(EntityVersion::CollisionMask16Bytes);
|
return static_cast<PacketVersion>(EntityVersion::YieldSimulationOwnership);
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
|
|
|
@ -236,7 +236,8 @@ enum class EntityVersion : PacketVersion {
|
||||||
ShadowControl,
|
ShadowControl,
|
||||||
MaterialData,
|
MaterialData,
|
||||||
CloneableData,
|
CloneableData,
|
||||||
CollisionMask16Bytes
|
CollisionMask16Bytes,
|
||||||
|
YieldSimulationOwnership
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EntityScriptCallMethodVersion : PacketVersion {
|
enum class EntityScriptCallMethodVersion : PacketVersion {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
set(TARGET_NAME physics)
|
set(TARGET_NAME physics)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
link_hifi_libraries(shared fbx entities graphics)
|
link_hifi_libraries(shared task workload fbx entities graphics)
|
||||||
include_hifi_library_headers(networking)
|
include_hifi_library_headers(networking)
|
||||||
include_hifi_library_headers(gpu)
|
include_hifi_library_headers(gpu)
|
||||||
include_hifi_library_headers(avatars)
|
include_hifi_library_headers(avatars)
|
||||||
|
|
|
@ -80,7 +80,6 @@ EntityMotionState::EntityMotionState(btCollisionShape* shape, EntityItemPointer
|
||||||
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
// rather than pass the legit shape pointer to the ObjectMotionState ctor above.
|
||||||
setShape(shape);
|
setShape(shape);
|
||||||
|
|
||||||
_bidPriority = _entity->getPendingOwnershipPriority();
|
|
||||||
if (_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) {
|
if (_entity->getClientOnly() && _entity->getOwningAvatarID() != Physics::getSessionUUID()) {
|
||||||
// client-only entities are always thus, so we cache this fact in _ownershipState
|
// client-only entities are always thus, so we cache this fact in _ownershipState
|
||||||
_ownershipState = EntityMotionState::OwnershipState::Unownable;
|
_ownershipState = EntityMotionState::OwnershipState::Unownable;
|
||||||
|
@ -140,28 +139,22 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
if (_entity->getSimulatorID().isNull()) {
|
if (_entity->getSimulatorID().isNull()) {
|
||||||
// simulation ownership has been removed
|
// simulation ownership has been removed
|
||||||
if (glm::length2(_entity->getWorldVelocity()) == 0.0f) {
|
if (glm::length2(_entity->getWorldVelocity()) == 0.0f) {
|
||||||
// this object is coming to rest --> clear the ACTIVATION flag and _bidPriority
|
// TODO: also check angularVelocity
|
||||||
|
// this object is coming to rest
|
||||||
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
|
flags &= ~Simulation::DIRTY_PHYSICS_ACTIVATION;
|
||||||
_body->setActivationState(WANTS_DEACTIVATION);
|
_body->setActivationState(WANTS_DEACTIVATION);
|
||||||
_bidPriority = 0;
|
|
||||||
const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet
|
const float ACTIVATION_EXPIRY = 3.0f; // something larger than the 2.0 hard coded in Bullet
|
||||||
_body->setDeactivationTime(ACTIVATION_EXPIRY);
|
_body->setDeactivationTime(ACTIVATION_EXPIRY);
|
||||||
} else {
|
} else {
|
||||||
// disowned object is still moving --> start timer for ownership bid
|
// disowned object is still moving --> start timer for ownership bid
|
||||||
// TODO? put a delay in here proportional to distance from object?
|
// TODO? put a delay in here proportional to distance from object?
|
||||||
upgradeBidPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
_bumpedPriority = glm::max(_bumpedPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
||||||
_nextBidExpiry = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
_nextBidExpiry = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||||
}
|
}
|
||||||
_loopsWithoutOwner = 0;
|
_loopsWithoutOwner = 0;
|
||||||
_numInactiveUpdates = 0;
|
_numInactiveUpdates = 0;
|
||||||
} else if (isLocallyOwned()) {
|
} else if (!isLocallyOwned()) {
|
||||||
// we just inherited ownership, make sure our desired priority matches what we have
|
// the entity is owned by someone else
|
||||||
upgradeBidPriority(_entity->getSimulationPriority());
|
|
||||||
} else {
|
|
||||||
// the entity is owned by someone else, so we clear _bidPriority here
|
|
||||||
// but _bidPriority may be updated to non-zero value if this object interacts with locally owned simulation
|
|
||||||
// in which case we may try to bid again
|
|
||||||
_bidPriority = 0;
|
|
||||||
_nextBidExpiry = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
_nextBidExpiry = usecTimestampNow() + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||||
_numInactiveUpdates = 0;
|
_numInactiveUpdates = 0;
|
||||||
}
|
}
|
||||||
|
@ -170,9 +163,6 @@ void EntityMotionState::handleEasyChanges(uint32_t& flags) {
|
||||||
// The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following:
|
// The DIRTY_SIMULATOR_OWNERSHIP_PRIORITY bit means one of the following:
|
||||||
// (1) we own it but may need to change the priority OR...
|
// (1) we own it but may need to change the priority OR...
|
||||||
// (2) we don't own it but should bid (because a local script has been changing physics properties)
|
// (2) we don't own it but should bid (because a local script has been changing physics properties)
|
||||||
uint8_t newPriority = isLocallyOwned() ? _entity->getSimulationOwner().getPriority() : _entity->getSimulationOwner().getPendingPriority();
|
|
||||||
upgradeBidPriority(newPriority);
|
|
||||||
|
|
||||||
// reset bid expiry so that we bid ASAP
|
// reset bid expiry so that we bid ASAP
|
||||||
_nextBidExpiry = 0;
|
_nextBidExpiry = 0;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +288,7 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||||
if (_entity->getSimulatorID().isNull()) {
|
if (_entity->getSimulatorID().isNull()) {
|
||||||
_loopsWithoutOwner++;
|
_loopsWithoutOwner++;
|
||||||
if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextBidExpiry) {
|
if (_loopsWithoutOwner > LOOPS_FOR_SIMULATION_ORPHAN && usecTimestampNow() > _nextBidExpiry) {
|
||||||
upgradeBidPriority(VOLUNTEER_SIMULATION_PRIORITY);
|
_bumpedPriority = glm::max(_bumpedPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,21 +315,23 @@ void EntityMotionState::setShape(const btCollisionShape* shape) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
// NOTE: we only get here if we think we own the simulation
|
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
DETAILED_PROFILE_RANGE(simulation_physics, "CheckOutOfSync");
|
||||||
|
|
||||||
// Since we own the simulation: make sure _bidPriority is not less than current owned priority
|
// Since we own the simulation: make sure _bidPriority is not less than current owned priority
|
||||||
// because: an _bidPriority of zero indicates that we should drop ownership when we have it.
|
// because: a _bidPriority of zero indicates that we should drop ownership in the send.
|
||||||
upgradeBidPriority(_entity->getSimulationPriority());
|
// TODO: need to be able to detect when logic dictates we *decrease* priority
|
||||||
|
// WIP: print info whenever _bidPriority mismatches what is known to the entity
|
||||||
|
|
||||||
|
if (_entity->dynamicDataNeedsTransmit()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool parentTransformSuccess;
|
bool parentTransformSuccess;
|
||||||
Transform localToWorld = _entity->getParentTransform(parentTransformSuccess);
|
Transform localToWorld = _entity->getParentTransform(parentTransformSuccess);
|
||||||
Transform worldToLocal;
|
Transform worldToLocal;
|
||||||
Transform worldVelocityToLocal;
|
|
||||||
if (parentTransformSuccess) {
|
if (parentTransformSuccess) {
|
||||||
localToWorld.evalInverse(worldToLocal);
|
localToWorld.evalInverse(worldToLocal);
|
||||||
worldVelocityToLocal = worldToLocal;
|
|
||||||
worldVelocityToLocal.setTranslation(glm::vec3(0.0f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int numSteps = simulationStep - _lastStep;
|
int numSteps = simulationStep - _lastStep;
|
||||||
|
@ -388,12 +380,6 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_entity->dynamicDataNeedsTransmit()) {
|
|
||||||
uint8_t priority = _entity->hasActions() ? SCRIPT_GRAB_SIMULATION_PRIORITY : SCRIPT_POKE_SIMULATION_PRIORITY;
|
|
||||||
upgradeBidPriority(priority);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
// Else we measure the error between current and extrapolated transform (according to expected behavior
|
||||||
// of remote EntitySimulation) and return true if the error is significant.
|
// of remote EntitySimulation) and return true if the error is significant.
|
||||||
|
|
||||||
|
@ -438,6 +424,7 @@ bool EntityMotionState::remoteSimulationOutOfSync(uint32_t simulationStep) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
bool EntityMotionState::shouldSendUpdate(uint32_t simulationStep) {
|
||||||
|
// NOTE: this method is only ever called when the entity simulation is locally owned
|
||||||
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
DETAILED_PROFILE_RANGE(simulation_physics, "ShouldSend");
|
||||||
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
// NOTE: we expect _entity and _body to be valid in this context, since shouldSendUpdate() is only called
|
||||||
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
// after doesNotNeedToSendUpdate() returns false and that call should return 'true' if _entity or _body are NULL.
|
||||||
|
@ -462,6 +449,7 @@ void EntityMotionState::updateSendVelocities() {
|
||||||
if (!_body->isActive()) {
|
if (!_body->isActive()) {
|
||||||
// make sure all derivatives are zero
|
// make sure all derivatives are zero
|
||||||
clearObjectVelocities();
|
clearObjectVelocities();
|
||||||
|
// we pretend we sent the inactive update for this object
|
||||||
_numInactiveUpdates = 1;
|
_numInactiveUpdates = 1;
|
||||||
} else {
|
} else {
|
||||||
glm::vec3 gravity = _entity->getGravity();
|
glm::vec3 gravity = _entity->getGravity();
|
||||||
|
@ -526,10 +514,10 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
||||||
properties.setLastEdited(now);
|
properties.setLastEdited(now);
|
||||||
|
|
||||||
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
// we don't own the simulation for this entity yet, but we're sending a bid for it
|
||||||
uint8_t bidPriority = glm::max<uint8_t>(_bidPriority, VOLUNTEER_SIMULATION_PRIORITY);
|
uint8_t finalBidPriority = computeFinalBidPriority();
|
||||||
properties.setSimulationOwner(Physics::getSessionUUID(), bidPriority);
|
_entity->clearScriptSimulationPriority();
|
||||||
// copy _bidPriority into pendingPriority...
|
properties.setSimulationOwner(Physics::getSessionUUID(), finalBidPriority);
|
||||||
_entity->setPendingOwnershipPriority(_bidPriority, now);
|
_entity->setPendingOwnershipPriority(finalBidPriority);
|
||||||
|
|
||||||
EntityTreeElementPointer element = _entity->getElement();
|
EntityTreeElementPointer element = _entity->getElement();
|
||||||
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
EntityTreePointer tree = element ? element->getTree() : nullptr;
|
||||||
|
@ -548,10 +536,10 @@ void EntityMotionState::sendBid(OctreeEditPacketSender* packetSender, uint32_t s
|
||||||
_lastStep = step;
|
_lastStep = step;
|
||||||
_nextBidExpiry = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
_nextBidExpiry = now + USECS_BETWEEN_OWNERSHIP_BIDS;
|
||||||
|
|
||||||
// finally: clear _bidPriority
|
// after sending a bid/update we clear _bumpedPriority
|
||||||
// which will may get promoted before next bid
|
// which might get promoted again next frame (after local script or simulation interaction)
|
||||||
// or maybe we'll win simulation ownership
|
// or we might win the bid
|
||||||
_bidPriority = 0;
|
_bumpedPriority = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t step) {
|
||||||
|
@ -590,22 +578,30 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
properties.setLastEdited(now);
|
properties.setLastEdited(now);
|
||||||
_entity->setSimulationOwnershipExpiry(now + MAX_OUTGOING_SIMULATION_UPDATE_PERIOD);
|
_entity->setSimulationOwnershipExpiry(now + MAX_OUTGOING_SIMULATION_UPDATE_PERIOD);
|
||||||
|
|
||||||
if (_numInactiveUpdates > 0) {
|
if (_numInactiveUpdates > 0 && _entity->getScriptSimulationPriority() == 0) {
|
||||||
// the entity is stopped and inactive so we tell the server we're clearing simulatorID
|
// the entity is stopped and inactive so we tell the server we're clearing simulatorID
|
||||||
// but we remember we do still own it... and rely on the server to tell us we don't
|
// but we remember we do still own it... and rely on the server to tell us we don't
|
||||||
properties.clearSimulationOwner();
|
properties.clearSimulationOwner();
|
||||||
_bidPriority = 0;
|
_entity->setPendingOwnershipPriority(0);
|
||||||
_entity->setPendingOwnershipPriority(_bidPriority, now);
|
} else {
|
||||||
} else if (_bidPriority != _entity->getSimulationPriority()) {
|
uint8_t newPriority = computeFinalBidPriority();
|
||||||
// our desired priority has changed
|
_entity->clearScriptSimulationPriority();
|
||||||
if (_bidPriority == 0) {
|
// if we get here then we own the simulation and the object is NOT going inactive
|
||||||
// we should release ownership
|
// if newPriority is zero, then it must be outside of R1, which means we should really set it to YIELD
|
||||||
properties.clearSimulationOwner();
|
// which we achive by just setting it to the max of the two
|
||||||
} else {
|
newPriority = glm::max(newPriority, YIELD_SIMULATION_PRIORITY);
|
||||||
// we just need to change the priority
|
if (newPriority != _entity->getSimulationPriority() &&
|
||||||
properties.setSimulationOwner(Physics::getSessionUUID(), _bidPriority);
|
!(newPriority == VOLUNTEER_SIMULATION_PRIORITY && _entity->getSimulationPriority() == RECRUIT_SIMULATION_PRIORITY)) {
|
||||||
|
// our desired priority has changed
|
||||||
|
if (newPriority == 0) {
|
||||||
|
// we should release ownership
|
||||||
|
properties.clearSimulationOwner();
|
||||||
|
} else {
|
||||||
|
// we just need to inform the entity-server
|
||||||
|
properties.setSimulationOwner(Physics::getSessionUUID(), newPriority);
|
||||||
|
}
|
||||||
|
_entity->setPendingOwnershipPriority(newPriority);
|
||||||
}
|
}
|
||||||
_entity->setPendingOwnershipPriority(_bidPriority, now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityItemID id(_entity->getID());
|
EntityItemID id(_entity->getID());
|
||||||
|
@ -640,6 +636,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
|
||||||
});
|
});
|
||||||
|
|
||||||
_lastStep = step;
|
_lastStep = step;
|
||||||
|
|
||||||
|
// after sending a bid/update we clear _bumpedPriority
|
||||||
|
// which might get promoted again next frame (after local script or simulation interaction)
|
||||||
|
// or we might win the bid
|
||||||
|
_bumpedPriority = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
uint32_t EntityMotionState::getIncomingDirtyFlags() {
|
||||||
|
@ -686,7 +687,7 @@ uint8_t EntityMotionState::getSimulationPriority() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::slaveBidPriority() {
|
void EntityMotionState::slaveBidPriority() {
|
||||||
upgradeBidPriority(_entity->getSimulationPriority());
|
_bumpedPriority = glm::max(_bumpedPriority, _entity->getSimulationPriority());
|
||||||
}
|
}
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
|
@ -697,7 +698,7 @@ QUuid EntityMotionState::getSimulatorID() const {
|
||||||
|
|
||||||
void EntityMotionState::bump(uint8_t priority) {
|
void EntityMotionState::bump(uint8_t priority) {
|
||||||
assert(priority != 0);
|
assert(priority != 0);
|
||||||
upgradeBidPriority(glm::max(VOLUNTEER_SIMULATION_PRIORITY, --priority));
|
_bumpedPriority = glm::max(_bumpedPriority, --priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
void EntityMotionState::resetMeasuredBodyAcceleration() {
|
||||||
|
@ -765,7 +766,6 @@ void EntityMotionState::setMotionType(PhysicsMotionType motionType) {
|
||||||
resetMeasuredBodyAcceleration();
|
resetMeasuredBodyAcceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// virtual
|
// virtual
|
||||||
QString EntityMotionState::getName() const {
|
QString EntityMotionState::getName() const {
|
||||||
assert(entityTreeIsLocked());
|
assert(entityTreeIsLocked());
|
||||||
|
@ -777,15 +777,15 @@ void EntityMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma
|
||||||
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
_entity->computeCollisionGroupAndFinalMask(group, mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::shouldSendBid() {
|
bool EntityMotionState::shouldSendBid() const {
|
||||||
if (_bidPriority >= glm::max(_entity->getSimulationPriority(), VOLUNTEER_SIMULATION_PRIORITY)) {
|
// NOTE: this method is only ever called when the entity's simulation is NOT locally owned
|
||||||
return true;
|
return _body->isActive() && (_region == workload::Region::R1) &&
|
||||||
} else {
|
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) >= _entity->getSimulationPriority();
|
||||||
// NOTE: this 'else' case has a side-effect: it clears _bidPriority
|
}
|
||||||
// which may be updated next simulation step (via collision or script event)
|
|
||||||
_bidPriority = 0;
|
uint8_t EntityMotionState::computeFinalBidPriority() const {
|
||||||
return false;
|
return (_region == workload::Region::R1) ?
|
||||||
}
|
glm::max(glm::max(VOLUNTEER_SIMULATION_PRIORITY, _bumpedPriority), _entity->getScriptSimulationPriority()) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::isLocallyOwned() const {
|
bool EntityMotionState::isLocallyOwned() const {
|
||||||
|
@ -793,8 +793,17 @@ bool EntityMotionState::isLocallyOwned() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EntityMotionState::isLocallyOwnedOrShouldBe() const {
|
bool EntityMotionState::isLocallyOwnedOrShouldBe() const {
|
||||||
return (_bidPriority > VOLUNTEER_SIMULATION_PRIORITY && _bidPriority > _entity->getSimulationPriority()) ||
|
// this method could also be called "shouldGenerateCollisionEventForLocalScripts()"
|
||||||
_entity->getSimulatorID() == Physics::getSessionUUID();
|
// because that is the only reason it's used
|
||||||
|
if (_entity->getSimulatorID() == Physics::getSessionUUID()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return computeFinalBidPriority() > glm::max(VOLUNTEER_SIMULATION_PRIORITY, _entity->getSimulationPriority());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntityMotionState::setRegion(uint8_t region) {
|
||||||
|
_region = region;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::initForBid() {
|
void EntityMotionState::initForBid() {
|
||||||
|
@ -807,10 +816,6 @@ void EntityMotionState::initForOwned() {
|
||||||
_ownershipState = EntityMotionState::OwnershipState::LocallyOwned;
|
_ownershipState = EntityMotionState::OwnershipState::LocallyOwned;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityMotionState::upgradeBidPriority(uint8_t priority) {
|
|
||||||
_bidPriority = glm::max<uint8_t>(_bidPriority, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EntityMotionState::clearObjectVelocities() const {
|
void EntityMotionState::clearObjectVelocities() const {
|
||||||
// If transform or velocities are flagged as dirty it means a network or scripted change
|
// If transform or velocities are flagged as dirty it means a network or scripted change
|
||||||
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
// occured between the beginning and end of the stepSimulation() and we DON'T want to apply
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <EntityTypes.h>
|
#include <EntityTypes.h>
|
||||||
#include <AACube.h>
|
#include <AACube.h>
|
||||||
|
#include <workload/Region.h>
|
||||||
|
|
||||||
#include "ObjectMotionState.h"
|
#include "ObjectMotionState.h"
|
||||||
|
|
||||||
|
@ -86,13 +87,17 @@ public:
|
||||||
|
|
||||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||||
|
|
||||||
bool shouldSendBid();
|
bool shouldSendBid() const;
|
||||||
|
uint8_t computeFinalBidPriority() const;
|
||||||
|
|
||||||
bool isLocallyOwned() const override;
|
bool isLocallyOwned() const override;
|
||||||
bool isLocallyOwnedOrShouldBe() const override; // aka shouldEmitCollisionEvents()
|
bool isLocallyOwnedOrShouldBe() const override; // aka shouldEmitCollisionEvents()
|
||||||
|
|
||||||
friend class PhysicalEntitySimulation;
|
friend class PhysicalEntitySimulation;
|
||||||
OwnershipState getOwnershipState() const { return _ownershipState; }
|
OwnershipState getOwnershipState() const { return _ownershipState; }
|
||||||
|
|
||||||
|
void setRegion(uint8_t region);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void updateSendVelocities();
|
void updateSendVelocities();
|
||||||
uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
|
uint64_t getNextBidExpiry() const { return _nextBidExpiry; }
|
||||||
|
@ -102,11 +107,7 @@ protected:
|
||||||
void updateServerPhysicsVariables();
|
void updateServerPhysicsVariables();
|
||||||
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
bool remoteSimulationOutOfSync(uint32_t simulationStep);
|
||||||
|
|
||||||
// changes _bidPriority only if priority is larger
|
void slaveBidPriority(); // computeNewBidPriority() with value stored in _entity
|
||||||
void upgradeBidPriority(uint8_t priority);
|
|
||||||
|
|
||||||
// upgradeBidPriority to value stored in _entity
|
|
||||||
void slaveBidPriority();
|
|
||||||
|
|
||||||
void clearObjectVelocities() const;
|
void clearObjectVelocities() const;
|
||||||
|
|
||||||
|
@ -154,8 +155,8 @@ protected:
|
||||||
uint8_t _loopsWithoutOwner;
|
uint8_t _loopsWithoutOwner;
|
||||||
mutable uint8_t _accelerationNearlyGravityCount;
|
mutable uint8_t _accelerationNearlyGravityCount;
|
||||||
uint8_t _numInactiveUpdates { 1 };
|
uint8_t _numInactiveUpdates { 1 };
|
||||||
uint8_t _bidPriority { 0 };
|
uint8_t _bumpedPriority { 0 }; // the target simulation priority according to collision history
|
||||||
bool _serverVariablesSet { false };
|
uint8_t _region { workload::Region::INVALID };
|
||||||
|
|
||||||
bool isServerlessMode();
|
bool isServerlessMode();
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,12 +48,17 @@ void PhysicalEntitySimulation::addEntityInternal(EntityItemPointer entity) {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
assert(entity);
|
assert(entity);
|
||||||
assert(!entity->isDead());
|
assert(!entity->isDead());
|
||||||
if (entity->shouldBePhysical()) {
|
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
||||||
|
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
||||||
|
bool canBeKinematic = region <= workload::Region::R3;
|
||||||
|
if (shouldBePhysical) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
if (!motionState) {
|
if (motionState) {
|
||||||
|
motionState->setRegion(region);
|
||||||
|
} else {
|
||||||
_entitiesToAddToPhysics.insert(entity);
|
_entitiesToAddToPhysics.insert(entity);
|
||||||
}
|
}
|
||||||
} else if (entity->isMovingRelativeToParent()) {
|
} else if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||||
_simpleKinematicEntities.insert(entity);
|
_simpleKinematicEntities.insert(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,25 +125,43 @@ void PhysicalEntitySimulation::changeEntityInternal(EntityItemPointer entity) {
|
||||||
QMutexLocker lock(&_mutex);
|
QMutexLocker lock(&_mutex);
|
||||||
assert(entity);
|
assert(entity);
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
|
uint8_t region = _space->getRegion(entity->getSpaceIndex());
|
||||||
|
bool shouldBePhysical = region < workload::Region::R3 && entity->shouldBePhysical();
|
||||||
|
bool canBeKinematic = region <= workload::Region::R3;
|
||||||
if (motionState) {
|
if (motionState) {
|
||||||
if (!entity->shouldBePhysical()) {
|
if (!shouldBePhysical) {
|
||||||
// the entity should be removed from the physical simulation
|
if (motionState->isLocallyOwned()) {
|
||||||
|
// zero velocities by first deactivating the RigidBody
|
||||||
|
btRigidBody* body = motionState->getRigidBody();
|
||||||
|
if (body) {
|
||||||
|
body->forceActivationState(ISLAND_SLEEPING);
|
||||||
|
motionState->updateSendVelocities(); // has side-effect of zeroing entity velocities for inactive body
|
||||||
|
}
|
||||||
|
|
||||||
|
// send packet to remove ownership
|
||||||
|
// NOTE: this packet will NOT be resent if lost, but the good news is:
|
||||||
|
// the entity-server will eventually clear velocity and ownership for timeout
|
||||||
|
motionState->sendUpdate(_entityPacketSender, _physicsEngine->getNumSubsteps());
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove from the physical simulation
|
||||||
_incomingChanges.remove(motionState);
|
_incomingChanges.remove(motionState);
|
||||||
_physicalObjects.remove(motionState);
|
_physicalObjects.remove(motionState);
|
||||||
removeOwnershipData(motionState);
|
removeOwnershipData(motionState);
|
||||||
_entitiesToRemoveFromPhysics.insert(entity);
|
_entitiesToRemoveFromPhysics.insert(entity);
|
||||||
if (entity->isMovingRelativeToParent()) {
|
if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||||
_simpleKinematicEntities.insert(entity);
|
_simpleKinematicEntities.insert(entity);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_incomingChanges.insert(motionState);
|
_incomingChanges.insert(motionState);
|
||||||
}
|
}
|
||||||
} else if (entity->shouldBePhysical()) {
|
motionState->setRegion(region);
|
||||||
|
} else if (shouldBePhysical) {
|
||||||
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
|
// The intent is for this object to be in the PhysicsEngine, but it has no MotionState yet.
|
||||||
// Perhaps it's shape has changed and it can now be added?
|
// Perhaps it's shape has changed and it can now be added?
|
||||||
_entitiesToAddToPhysics.insert(entity);
|
_entitiesToAddToPhysics.insert(entity);
|
||||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||||
} else if (entity->isMovingRelativeToParent()) {
|
} else if (canBeKinematic && entity->isMovingRelativeToParent()) {
|
||||||
_simpleKinematicEntities.insert(entity);
|
_simpleKinematicEntities.insert(entity);
|
||||||
} else {
|
} else {
|
||||||
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
_simpleKinematicEntities.remove(entity); // just in case it's non-physical-kinematic
|
||||||
|
@ -187,18 +210,21 @@ const VectorOfMotionStates& PhysicalEntitySimulation::getObjectsToRemoveFromPhys
|
||||||
for (auto entity: _entitiesToRemoveFromPhysics) {
|
for (auto entity: _entitiesToRemoveFromPhysics) {
|
||||||
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
EntityMotionState* motionState = static_cast<EntityMotionState*>(entity->getPhysicsInfo());
|
||||||
assert(motionState);
|
assert(motionState);
|
||||||
|
// TODO CLEan this, just a n extra check to avoid the crash that shouldn;t happen
|
||||||
|
if (motionState) {
|
||||||
|
|
||||||
_entitiesToAddToPhysics.remove(entity);
|
_entitiesToAddToPhysics.remove(entity);
|
||||||
if (entity->isDead() && entity->getElement()) {
|
if (entity->isDead() && entity->getElement()) {
|
||||||
_deadEntities.insert(entity);
|
_deadEntities.insert(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
_incomingChanges.remove(motionState);
|
||||||
|
removeOwnershipData(motionState);
|
||||||
|
_physicalObjects.remove(motionState);
|
||||||
|
|
||||||
|
// remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine)
|
||||||
|
_objectsToDelete.push_back(motionState);
|
||||||
}
|
}
|
||||||
|
|
||||||
_incomingChanges.remove(motionState);
|
|
||||||
removeOwnershipData(motionState);
|
|
||||||
_physicalObjects.remove(motionState);
|
|
||||||
|
|
||||||
// remember this motionState and delete it later (after removing its RigidBody from the PhysicsEngine)
|
|
||||||
_objectsToDelete.push_back(motionState);
|
|
||||||
}
|
}
|
||||||
_entitiesToRemoveFromPhysics.clear();
|
_entitiesToRemoveFromPhysics.clear();
|
||||||
return _objectsToDelete;
|
return _objectsToDelete;
|
||||||
|
@ -249,6 +275,9 @@ void PhysicalEntitySimulation::getObjectsToAddToPhysics(VectorOfMotionStates& re
|
||||||
_physicalObjects.insert(motionState);
|
_physicalObjects.insert(motionState);
|
||||||
result.push_back(motionState);
|
result.push_back(motionState);
|
||||||
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
entityItr = _entitiesToAddToPhysics.erase(entityItr);
|
||||||
|
|
||||||
|
// make sure the motionState's region is up-to-date before it is actually added to physics
|
||||||
|
motionState->setRegion(_space->getRegion(entity->getSpaceIndex()));
|
||||||
} else {
|
} else {
|
||||||
//qWarning() << "Failed to generate new shape for entity." << entity->getName();
|
//qWarning() << "Failed to generate new shape for entity." << entity->getName();
|
||||||
++entityItr;
|
++entityItr;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include <EntityItem.h>
|
#include <EntityItem.h>
|
||||||
#include <EntitySimulation.h>
|
#include <EntitySimulation.h>
|
||||||
|
#include <workload/Space.h>
|
||||||
|
|
||||||
#include "PhysicsEngine.h"
|
#include "PhysicsEngine.h"
|
||||||
#include "EntityMotionState.h"
|
#include "EntityMotionState.h"
|
||||||
|
@ -45,6 +46,7 @@ public:
|
||||||
~PhysicalEntitySimulation();
|
~PhysicalEntitySimulation();
|
||||||
|
|
||||||
void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
|
void init(EntityTreePointer tree, PhysicsEnginePointer engine, EntityEditPacketSender* packetSender);
|
||||||
|
void setWorkloadSpace(const workload::SpacePointer space) { _space = space; }
|
||||||
|
|
||||||
virtual void addDynamic(EntityDynamicPointer dynamic) override;
|
virtual void addDynamic(EntityDynamicPointer dynamic) override;
|
||||||
virtual void applyDynamicChanges() override;
|
virtual void applyDynamicChanges() override;
|
||||||
|
@ -102,6 +104,7 @@ private:
|
||||||
|
|
||||||
VectorOfEntityMotionStates _owned;
|
VectorOfEntityMotionStates _owned;
|
||||||
VectorOfEntityMotionStates _bids;
|
VectorOfEntityMotionStates _bids;
|
||||||
|
workload::SpacePointer _space;
|
||||||
uint64_t _nextBidExpiry;
|
uint64_t _nextBidExpiry;
|
||||||
uint32_t _lastStepSendPackets { 0 };
|
uint32_t _lastStepSendPackets { 0 };
|
||||||
};
|
};
|
||||||
|
|
32
libraries/render-utils/src/drawWorkloadProxy.slf
Normal file
32
libraries/render-utils/src/drawWorkloadProxy.slf
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<@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
|
||||||
|
//
|
||||||
|
<@include DeferredBufferWrite.slh@>
|
||||||
|
<@include gpu/Paint.slh@>
|
||||||
|
|
||||||
|
in vec4 varColor;
|
||||||
|
in vec3 varTexcoord;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
if (varColor.w > 0.0) {
|
||||||
|
float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz));
|
||||||
|
float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w);
|
||||||
|
if (a <= 0.1 || r > 1.1) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packDeferredFragmentUnlit(
|
||||||
|
vec3(0.0, 1.0, 0.0),
|
||||||
|
1.0,
|
||||||
|
varColor.rgb);
|
||||||
|
}
|
94
libraries/render-utils/src/drawWorkloadProxy.slv
Normal file
94
libraries/render-utils/src/drawWorkloadProxy.slv
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
<@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()$>
|
||||||
|
|
||||||
|
<@include gpu/Color.slh@>
|
||||||
|
<$declareColorWheel()$>
|
||||||
|
|
||||||
|
uniform vec4 inColor;
|
||||||
|
|
||||||
|
|
||||||
|
struct WorkloadProxy {
|
||||||
|
vec4 sphere;
|
||||||
|
vec4 region;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(GPU_GL410)
|
||||||
|
uniform samplerBuffer workloadProxiesBuffer;
|
||||||
|
WorkloadProxy getWorkloadProxy(int i) {
|
||||||
|
int offset = 2 * i;
|
||||||
|
WorkloadProxy proxy;
|
||||||
|
proxy.sphere = texelFetch(workloadProxiesBuffer, offset);
|
||||||
|
proxy.region = texelFetch(workloadProxiesBuffer, offset + 1);
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
layout(std140) buffer workloadProxiesBuffer {
|
||||||
|
WorkloadProxy _proxies[];
|
||||||
|
};
|
||||||
|
WorkloadProxy getWorkloadProxy(int i) {
|
||||||
|
WorkloadProxy proxy = _proxies[i];
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
out vec4 varColor;
|
||||||
|
out vec3 varTexcoord;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
const vec4 UNIT_SPRITE[3] = vec4[3](
|
||||||
|
vec4(-1.0, -1.0, 0.0, 1.0),
|
||||||
|
vec4(3.0, -1.0, 0.0, 1.0),
|
||||||
|
vec4(-1.0, 3.0, 0.0, 1.0)
|
||||||
|
);
|
||||||
|
const int UNIT_SPRITE_INDICES[3] = int[3](
|
||||||
|
0, 1, 2
|
||||||
|
);
|
||||||
|
int proxyID = gl_VertexID / 3;
|
||||||
|
int vertexID = gl_VertexID - proxyID * 3;
|
||||||
|
|
||||||
|
vec4 spriteVert = UNIT_SPRITE[UNIT_SPRITE_INDICES[vertexID]];
|
||||||
|
|
||||||
|
WorkloadProxy proxy = getWorkloadProxy(proxyID);
|
||||||
|
vec4 proxyPosWorld = vec4(proxy.sphere.xyz, 1.0);
|
||||||
|
|
||||||
|
// standard transform, bring proxy in view space
|
||||||
|
TransformCamera cam = getTransformCamera();
|
||||||
|
TransformObject obj = getTransformObject();
|
||||||
|
vec4 proxyPosEye;
|
||||||
|
<$transformModelToEyePos(cam, obj, proxyPosWorld, proxyPosEye)$>
|
||||||
|
|
||||||
|
// Define the billboarded space
|
||||||
|
vec3 dirZ = -normalize(proxyPosEye.xyz);
|
||||||
|
vec3 dirX = normalize(cross(vec3(0.0, 1.0, 0.0), dirZ));
|
||||||
|
vec3 dirY = vec3(0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
vec4 pos = vec4(proxyPosEye.xyz + proxy.sphere.w * ( dirX * spriteVert.x + dirY * spriteVert.y /* + dirZ * spriteVert.z*/), 1.0);
|
||||||
|
varTexcoord = spriteVert.xyz;
|
||||||
|
<$transformEyeToClipPos(cam, pos, gl_Position)$>
|
||||||
|
|
||||||
|
// Convert region to color
|
||||||
|
int region = floatBitsToInt(proxy.region.x);
|
||||||
|
region = (0x000000FF & region);
|
||||||
|
|
||||||
|
varColor = vec4(colorWheel(float(region) / 4.0), proxy.sphere.w);
|
||||||
|
|
||||||
|
if (region == 4) {
|
||||||
|
gl_Position = vec4(0.0);
|
||||||
|
}
|
||||||
|
}
|
32
libraries/render-utils/src/drawWorkloadView.slf
Normal file
32
libraries/render-utils/src/drawWorkloadView.slf
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<@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
|
||||||
|
//
|
||||||
|
<@include DeferredBufferWrite.slh@>
|
||||||
|
<@include gpu/Paint.slh@>
|
||||||
|
|
||||||
|
in vec4 varColor;
|
||||||
|
in vec3 varTexcoord;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
if (varColor.w > 0.0) {
|
||||||
|
float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz));
|
||||||
|
float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w);
|
||||||
|
if (a <= 0.1 || r > 1.1) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
packDeferredFragmentUnlit(
|
||||||
|
vec3(0.0, 1.0, 0.0),
|
||||||
|
1.0,
|
||||||
|
varColor.rgb);
|
||||||
|
}
|
120
libraries/render-utils/src/drawWorkloadView.slv
Normal file
120
libraries/render-utils/src/drawWorkloadView.slv
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<@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()$>
|
||||||
|
|
||||||
|
<@include gpu/Color.slh@>
|
||||||
|
<$declareColorWheel()$>
|
||||||
|
|
||||||
|
uniform vec4 inColor;
|
||||||
|
|
||||||
|
|
||||||
|
struct WorkloadView {
|
||||||
|
vec4 direction_far;
|
||||||
|
vec4 fov;
|
||||||
|
vec4 origin;
|
||||||
|
vec4 backFront[2];
|
||||||
|
vec4 regions[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(GPU_GL410)
|
||||||
|
uniform samplerBuffer workloadViewsBuffer;
|
||||||
|
WorkloadView getWorkloadView(int i) {
|
||||||
|
int offset = 2 * i;
|
||||||
|
WorkloadView view;
|
||||||
|
view.origin = texelFetch(workloadViewsBuffer, offset);
|
||||||
|
view.radiuses = texelFetch(workloadViewsBuffer, offset + 1);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
layout(std140) buffer workloadViewsBuffer {
|
||||||
|
WorkloadView _views[];
|
||||||
|
};
|
||||||
|
WorkloadView getWorkloadView(int i) {
|
||||||
|
WorkloadView view = _views[i];
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
out vec4 varColor;
|
||||||
|
out vec3 varTexcoord;
|
||||||
|
|
||||||
|
const int NUM_VERTICES_PER_SEGMENT = 2;
|
||||||
|
const int NUM_SEGMENT_PER_VIEW_REGION = 65;
|
||||||
|
const int NUM_VERTICES_PER_VIEW_REGION = NUM_SEGMENT_PER_VIEW_REGION * NUM_VERTICES_PER_SEGMENT;
|
||||||
|
const int NUM_REGIONS_PER_VIEW = 3;
|
||||||
|
const int NUM_VERTICES_PER_VIEW = NUM_VERTICES_PER_VIEW_REGION * NUM_REGIONS_PER_VIEW;
|
||||||
|
|
||||||
|
|
||||||
|
layout(std140) uniform drawMeshBuffer {
|
||||||
|
vec4 verts[NUM_SEGMENT_PER_VIEW_REGION];
|
||||||
|
};
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
int viewID = gl_VertexID / NUM_VERTICES_PER_VIEW;
|
||||||
|
int viewVertexID = gl_VertexID - viewID * NUM_VERTICES_PER_VIEW;
|
||||||
|
|
||||||
|
int regionID = viewVertexID / NUM_VERTICES_PER_VIEW_REGION;
|
||||||
|
int regionVertexID = viewVertexID - regionID * NUM_VERTICES_PER_VIEW_REGION;
|
||||||
|
|
||||||
|
int segmentID = regionVertexID / NUM_VERTICES_PER_SEGMENT;
|
||||||
|
int segmentVertexID = regionVertexID - segmentID * NUM_VERTICES_PER_SEGMENT;
|
||||||
|
|
||||||
|
vec4 segment = verts[segmentID];
|
||||||
|
|
||||||
|
vec4 spriteVert = vec4(segment.y, 0.0, segment.x, 1.0);
|
||||||
|
vec3 spriteTan = vec3(segment.x, 0.0, -segment.y);
|
||||||
|
|
||||||
|
vec3 lateralDir = vec3(0.0, -1.0 + 2.0 * float(segmentVertexID), 0.0);
|
||||||
|
|
||||||
|
WorkloadView view = getWorkloadView(viewID);
|
||||||
|
vec4 region = view.regions[regionID];
|
||||||
|
vec4 proxyPosWorld = vec4(region.xyz, 1.0);
|
||||||
|
float regionRadius = region.w;
|
||||||
|
|
||||||
|
// Define the sprite space
|
||||||
|
vec3 dirZ = -normalize(view.direction_far.xyz);
|
||||||
|
vec3 dirY = vec3(0.0, 1.0, 0.0);
|
||||||
|
vec3 dirX = normalize(cross(dirY, dirZ));
|
||||||
|
dirY = normalize(cross(dirZ, dirX));
|
||||||
|
|
||||||
|
spriteVert.xyz *= regionRadius;
|
||||||
|
vec3 originSpaceVert = (dirX * spriteVert.x + dirY * spriteVert.y + dirZ * spriteVert.z);
|
||||||
|
vec4 pos = vec4(proxyPosWorld.xyz + originSpaceVert, 1.0);
|
||||||
|
|
||||||
|
vec3 originSpaceTan = normalize(dirX * spriteTan.x + dirY * spriteTan.y + dirZ * spriteTan.z);
|
||||||
|
|
||||||
|
// standard transform, bring pos in view space
|
||||||
|
TransformCamera cam = getTransformCamera();
|
||||||
|
TransformObject obj = getTransformObject();
|
||||||
|
vec4 posEye;
|
||||||
|
<$transformModelToEyePos(cam, obj, pos, posEye)$>
|
||||||
|
vec3 tanEye;
|
||||||
|
<$transformModelToEyeDir(cam, obj, originSpaceTan, tanEye)$>
|
||||||
|
|
||||||
|
lateralDir = normalize(cross(vec3(0.0, 0.0, 1.0), normalize(tanEye)));
|
||||||
|
posEye.xyz += (0.05 * (regionID + 1)) * (-1.0 + 2.0 * float(segmentVertexID)) * lateralDir;
|
||||||
|
|
||||||
|
<$transformEyeToClipPos(cam, posEye, gl_Position)$>
|
||||||
|
|
||||||
|
varTexcoord = spriteVert.xyz;
|
||||||
|
|
||||||
|
// Convert region to color
|
||||||
|
varColor = vec4(colorWheel(float(regionID) / 4.0), -1.0);
|
||||||
|
}
|
|
@ -201,8 +201,6 @@ public:
|
||||||
class TaskConfig : public JobConfig {
|
class TaskConfig : public JobConfig {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
using QConfigPointer = std::shared_ptr<QObject>;
|
|
||||||
|
|
||||||
using Persistent = PersistentConfig<TaskConfig>;
|
using Persistent = PersistentConfig<TaskConfig>;
|
||||||
|
|
||||||
TaskConfig() = default;
|
TaskConfig() = default;
|
||||||
|
@ -231,7 +229,8 @@ public:
|
||||||
|
|
||||||
if (tokens.empty()) {
|
if (tokens.empty()) {
|
||||||
tokens.push_back(QString());
|
tokens.push_back(QString());
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
while (tokens.size() > 1) {
|
while (tokens.size() > 1) {
|
||||||
auto name = tokens.front();
|
auto name = tokens.front();
|
||||||
tokens.pop_front();
|
tokens.pop_front();
|
||||||
|
|
|
@ -179,6 +179,7 @@ public:
|
||||||
const std::string& getName() const { return _concept->getName(); }
|
const std::string& getName() const { return _concept->getName(); }
|
||||||
const Varying getInput() const { return _concept->getInput(); }
|
const Varying getInput() const { return _concept->getInput(); }
|
||||||
const Varying getOutput() const { return _concept->getOutput(); }
|
const Varying getOutput() const { return _concept->getOutput(); }
|
||||||
|
|
||||||
QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); }
|
QConfigPointer& getConfiguration() const { return _concept->getConfiguration(); }
|
||||||
void applyConfiguration() { return _concept->applyConfiguration(); }
|
void applyConfiguration() { return _concept->applyConfiguration(); }
|
||||||
|
|
||||||
|
@ -376,6 +377,7 @@ public:
|
||||||
using Context = JC;
|
using Context = JC;
|
||||||
using ContextPointer = std::shared_ptr<Context>;
|
using ContextPointer = std::shared_ptr<Context>;
|
||||||
using Config = TaskConfig;
|
using Config = TaskConfig;
|
||||||
|
|
||||||
using TaskType = Task<JC, TP>;
|
using TaskType = Task<JC, TP>;
|
||||||
using ConceptPointer = typename TaskType::ConceptPointer;
|
using ConceptPointer = typename TaskType::ConceptPointer;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
set(TARGET_NAME workload)
|
set(TARGET_NAME workload)
|
||||||
setup_hifi_library()
|
setup_hifi_library()
|
||||||
|
link_hifi_libraries(shared task)
|
||||||
link_hifi_libraries(shared)
|
|
||||||
|
|
||||||
|
|
19
libraries/workload/src/workload/Engine.cpp
Normal file
19
libraries/workload/src/workload/Engine.cpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// Engine.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.02.08
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
WorkloadContext::WorkloadContext(const SpacePointer& space) : task::JobContext(), _space(space) {}
|
||||||
|
} // namespace workload
|
||||||
|
|
43
libraries/workload/src/workload/Engine.h
Normal file
43
libraries/workload/src/workload/Engine.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
//
|
||||||
|
// Engine.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.02.08
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
|
// Simple plane class.
|
||||||
|
//
|
||||||
|
// 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_workload_Engine_h
|
||||||
|
#define hifi_workload_Engine_h
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
#include <task/Task.h>
|
||||||
|
|
||||||
|
#include "Space.h"
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
class WorkloadContext : public task::JobContext {
|
||||||
|
public:
|
||||||
|
WorkloadContext(const SpacePointer& space);
|
||||||
|
virtual ~WorkloadContext() {}
|
||||||
|
|
||||||
|
SpacePointer _space;
|
||||||
|
};
|
||||||
|
|
||||||
|
using WorkloadContextPointer = std::shared_ptr<WorkloadContext>;
|
||||||
|
|
||||||
|
Task_DeclareCategoryTimeProfilerClass(WorkloadTimeProfiler, trace_workload);
|
||||||
|
|
||||||
|
// Instanciate the specialized types of Job Engine and Task for the Workload context
|
||||||
|
Task_DeclareTypeAliases(WorkloadContext, WorkloadTimeProfiler)
|
||||||
|
using EnginePointer = std::shared_ptr<Engine>;
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_Space_h
|
59
libraries/workload/src/workload/Proxy.h
Normal file
59
libraries/workload/src/workload/Proxy.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//
|
||||||
|
// Proxy.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.01.30
|
||||||
|
// 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_workload_Proxy_h
|
||||||
|
#define hifi_workload_Proxy_h
|
||||||
|
|
||||||
|
#include "View.h"
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
|
||||||
|
class Owner {
|
||||||
|
public:
|
||||||
|
Owner() = default;
|
||||||
|
Owner(const Owner& other) = default;
|
||||||
|
Owner& operator=(const Owner& other) = default;
|
||||||
|
template <class T> Owner(const T& data) : _concept(std::make_shared<Model<T>>(data)) {}
|
||||||
|
~Owner() {}
|
||||||
|
template <class T> const T get() const { return std::static_pointer_cast<const Model<T>>(_concept)->_data; }
|
||||||
|
protected:
|
||||||
|
class Concept {
|
||||||
|
public:
|
||||||
|
virtual ~Concept() = default;
|
||||||
|
};
|
||||||
|
template <class T> class Model : public Concept {
|
||||||
|
public:
|
||||||
|
using Data = T;
|
||||||
|
Data _data;
|
||||||
|
Model(const Data& data) : _data(data) {}
|
||||||
|
virtual ~Model() = default;
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Concept> _concept;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Proxy {
|
||||||
|
public:
|
||||||
|
Proxy() : sphere(0.0f) {}
|
||||||
|
Proxy(const Sphere& s) : sphere(s) {}
|
||||||
|
|
||||||
|
Sphere sphere;
|
||||||
|
uint8_t region{ Region::INVALID };
|
||||||
|
uint8_t prevRegion{ Region::INVALID };
|
||||||
|
uint16_t _padding;
|
||||||
|
uint32_t _paddings[3];
|
||||||
|
|
||||||
|
using Vector = std::vector<Proxy>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_Proxy_h
|
74
libraries/workload/src/workload/Region.h
Normal file
74
libraries/workload/src/workload/Region.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// Region.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.05
|
||||||
|
// 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_workload_Region_h
|
||||||
|
#define hifi_workload_Region_h
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
|
||||||
|
class Region {
|
||||||
|
public:
|
||||||
|
using Type = uint8_t;
|
||||||
|
|
||||||
|
enum Name : uint8_t {
|
||||||
|
R1 = 0,
|
||||||
|
R2,
|
||||||
|
R3,
|
||||||
|
UNKNOWN,
|
||||||
|
INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t NUM_CLASSIFICATIONS = 4;
|
||||||
|
static const uint8_t NUM_TRANSITIONS = NUM_CLASSIFICATIONS * (NUM_CLASSIFICATIONS - 1);
|
||||||
|
|
||||||
|
static const uint8_t NUM_VIEW_REGIONS = (NUM_CLASSIFICATIONS - 1);
|
||||||
|
|
||||||
|
static uint8_t computeTransitionIndex(uint8_t prevIndex, uint8_t newIndex);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint8_t Region::computeTransitionIndex(uint8_t prevIndex, uint8_t newIndex) {
|
||||||
|
// given prevIndex and newIndex compute an index into the transition list,
|
||||||
|
// where the lists between unchanged indices don't exist (signaled by index = -1).
|
||||||
|
//
|
||||||
|
// Given an NxN array
|
||||||
|
// let p = i + N * j
|
||||||
|
//
|
||||||
|
// then k = -1 when i == j
|
||||||
|
// = p - (1 + p/(N+1)) when i != j
|
||||||
|
//
|
||||||
|
// i 0 1 2 3
|
||||||
|
// j +-------+-------+-------+-------+
|
||||||
|
// |p = 0 | 1 | 2 | 3 |
|
||||||
|
// 0 | | | | |
|
||||||
|
// |k = -1 | 0 | 1 | 2 |
|
||||||
|
// +-------+-------+-------+-------+
|
||||||
|
// | 4 | 5 | 6 | 7 |
|
||||||
|
// 1 | | | | |
|
||||||
|
// | 3 | -1 | 4 | 5 |
|
||||||
|
// +-------+-------+-------+-------+
|
||||||
|
// | 8 | 9 | 10 | 11 |
|
||||||
|
// 2 | | | | |
|
||||||
|
// | 6 | 7 | -1 | 8 |
|
||||||
|
// +-------+-------+-------+-------+
|
||||||
|
// | 12 | 13 | 14 | 15 |
|
||||||
|
// 3 | | | | |
|
||||||
|
// | 9 | 10 | 11 | -1 |
|
||||||
|
// +-------+-------+-------+-------+
|
||||||
|
uint8_t p = prevIndex + Region::NUM_CLASSIFICATIONS * newIndex;
|
||||||
|
if (0 == (p % (Region::NUM_CLASSIFICATIONS + 1))) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return p - (1 + p / (Region::NUM_CLASSIFICATIONS + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_Region_h
|
76
libraries/workload/src/workload/RegionState.cpp
Normal file
76
libraries/workload/src/workload/RegionState.cpp
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// RegionState.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.03.07
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
|
// Simple plane class.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "RegionState.h"
|
||||||
|
|
||||||
|
using namespace workload;
|
||||||
|
|
||||||
|
void RegionState::configure(const Config& config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void RegionState::run(const workload::WorkloadContextPointer& renderContext, const Inputs& inputs) {
|
||||||
|
// inputs is a vector of vectors of proxyId's:
|
||||||
|
//
|
||||||
|
// inputs[0] = vector of ids exiting region 0
|
||||||
|
// inputs[1] = vector of ids entering region 0
|
||||||
|
// ...
|
||||||
|
// inputs[2N] = vector of ids exiting region N
|
||||||
|
// inputs[2N + 1] = vector of ids entering region N
|
||||||
|
assert(inputs.size() == 2 * Region::UNKNOWN);
|
||||||
|
|
||||||
|
// The id's in each vector are sorted in ascending order
|
||||||
|
// because the source vectors are scanned in ascending order.
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < _state.size(); ++i) {
|
||||||
|
const IndexVector& going = inputs[2 * i];
|
||||||
|
const IndexVector& coming = inputs[2 * i + 1];
|
||||||
|
if (coming.size() == 0 && going.size() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (_state[i].empty()) {
|
||||||
|
assert(going.empty());
|
||||||
|
_state[i] = coming;
|
||||||
|
} else {
|
||||||
|
// NOTE: all vectors are sorted by proxyId!
|
||||||
|
// which means we can build the new vector by walking three vectors (going, current, coming) in one pass
|
||||||
|
IndexVector& oldState = _state[i];
|
||||||
|
IndexVector newState;
|
||||||
|
newState.reserve(oldState.size() - going.size() + coming.size());
|
||||||
|
uint32_t goingIndex = 0;
|
||||||
|
uint32_t comingIndex = 0;
|
||||||
|
for (uint32_t j = 0; j < oldState.size(); ++j) {
|
||||||
|
int32_t proxyId = oldState[j];
|
||||||
|
while (comingIndex < coming.size() && coming[comingIndex] < proxyId) {
|
||||||
|
newState.push_back(coming[comingIndex]);
|
||||||
|
++comingIndex;
|
||||||
|
}
|
||||||
|
if (goingIndex < going.size() && going[goingIndex] == proxyId) {
|
||||||
|
++goingIndex;
|
||||||
|
} else {
|
||||||
|
newState.push_back(proxyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(goingIndex == going.size());
|
||||||
|
while (comingIndex < coming.size()) {
|
||||||
|
newState.push_back(coming[comingIndex]);
|
||||||
|
++comingIndex;
|
||||||
|
}
|
||||||
|
oldState.swap(newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
|
||||||
|
config->setNum(0, (uint32_t) _state[0].size(), (uint32_t) _state[1].size(), (uint32_t) _state[2].size());
|
||||||
|
}
|
67
libraries/workload/src/workload/RegionState.h
Normal file
67
libraries/workload/src/workload/RegionState.h
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
//
|
||||||
|
// RegionState.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.03.07
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
|
// Simple plane class.
|
||||||
|
//
|
||||||
|
// 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_workload_RegionState_h
|
||||||
|
#define hifi_workload_RegionState_h
|
||||||
|
|
||||||
|
#include "Space.h"
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
class RegionStateConfig : public Job::Config{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(float numR0 READ getNumR0 NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float numR1 READ getNumR1 NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float numR2 READ getNumR2 NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float numR3 READ getNumR3 NOTIFY dirty)
|
||||||
|
public:
|
||||||
|
|
||||||
|
uint32_t getNumR0() const { return data.numR0; }
|
||||||
|
uint32_t getNumR1() const { return data.numR1; }
|
||||||
|
uint32_t getNumR2() const { return data.numR2; }
|
||||||
|
uint32_t getNumR3() const { return data.numR3; }
|
||||||
|
|
||||||
|
void setNum(const uint32_t r0, const uint32_t r1, const uint32_t r2, const uint32_t r3) {
|
||||||
|
data.numR0 = r0; data.numR1 = r1; data.numR2 = r2; data.numR3 = r3; emit dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
uint32_t numR0{ 0 };
|
||||||
|
uint32_t numR1{ 0 };
|
||||||
|
uint32_t numR2{ 0 };
|
||||||
|
uint32_t numR3{ 0 };
|
||||||
|
} data;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
};
|
||||||
|
|
||||||
|
class RegionState {
|
||||||
|
public:
|
||||||
|
using Config = RegionStateConfig;
|
||||||
|
using Inputs = IndexVectors;
|
||||||
|
using JobModel = workload::Job::ModelI<RegionState, Inputs, Config>;
|
||||||
|
|
||||||
|
RegionState() {
|
||||||
|
_state.resize(Region::UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const workload::WorkloadContextPointer& renderContext, const Inputs& inputs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IndexVectors _state;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // hifi_workload_RegionState_h
|
50
libraries/workload/src/workload/RegionTracker.cpp
Normal file
50
libraries/workload/src/workload/RegionTracker.cpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// RegionTracker.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.02.21
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
|
// Simple plane class.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
#include "RegionTracker.h"
|
||||||
|
|
||||||
|
#include "Region.h"
|
||||||
|
|
||||||
|
using namespace workload;
|
||||||
|
|
||||||
|
void RegionTracker::configure(const Config& config) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegionTracker::run(const WorkloadContextPointer& context, Outputs& outputs) {
|
||||||
|
auto& outChanges = outputs.edit0();
|
||||||
|
auto& outRegionChanges = outputs.edit1();
|
||||||
|
|
||||||
|
|
||||||
|
outChanges.clear();
|
||||||
|
outRegionChanges.clear();
|
||||||
|
|
||||||
|
auto space = context->_space;
|
||||||
|
if (space) {
|
||||||
|
//Changes changes;
|
||||||
|
space->categorizeAndGetChanges(outChanges);
|
||||||
|
|
||||||
|
// use exit/enter lists for each region less than Region::UNKNOWN
|
||||||
|
outRegionChanges.resize(2 * (workload::Region::NUM_CLASSIFICATIONS - 1));
|
||||||
|
for (uint32_t i = 0; i < outChanges.size(); ++i) {
|
||||||
|
Space::Change& change = outChanges[i];
|
||||||
|
if (change.prevRegion < Region::UNKNOWN) {
|
||||||
|
// EXIT list index = 2 * regionIndex
|
||||||
|
outRegionChanges[2 * change.prevRegion].push_back(change.proxyId);
|
||||||
|
}
|
||||||
|
if (change.region < Region::UNKNOWN) {
|
||||||
|
// ENTER list index = 2 * regionIndex + 1
|
||||||
|
outRegionChanges[2 * change.region + 1].push_back(change.proxyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
libraries/workload/src/workload/RegionTracker.h
Normal file
44
libraries/workload/src/workload/RegionTracker.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// RegionTracker.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.02.21
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Originally from lighthouse3d. Modified to utilize glm::vec3 and clean up to our coding standards
|
||||||
|
// Simple plane class.
|
||||||
|
//
|
||||||
|
// 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_workload_RegionTracker_h
|
||||||
|
#define hifi_workload_RegionTracker_h
|
||||||
|
|
||||||
|
#include "Space.h"
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
|
||||||
|
class RegionTrackerConfig : public Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
RegionTrackerConfig() : Job::Config(true) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class RegionTracker {
|
||||||
|
public:
|
||||||
|
using Config = RegionTrackerConfig;
|
||||||
|
using Outputs = VaryingSet2<Changes, IndexVectors>;
|
||||||
|
using JobModel = workload::Job::ModelO<RegionTracker, Outputs, Config>;
|
||||||
|
|
||||||
|
RegionTracker() {}
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const workload::WorkloadContextPointer& renderContext, Outputs& outputs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_RegionTracker_h
|
|
@ -13,72 +13,92 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "Space.h"
|
#include "Space.h"
|
||||||
|
#include <cstring>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
using namespace workload;
|
using namespace workload;
|
||||||
|
|
||||||
int32_t Space::createProxy(const Space::Sphere& newSphere) {
|
Space::Space() : Collection() {
|
||||||
if (_freeIndices.empty()) {
|
}
|
||||||
_proxies.emplace_back(Space::Proxy(newSphere));
|
|
||||||
return (int32_t)_proxies.size() - 1;
|
void Space::processTransactionFrame(const Transaction& transaction) {
|
||||||
} else {
|
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||||
int32_t index = _freeIndices.back();
|
// Here we should be able to check the value of last ProxyID allocated
|
||||||
_freeIndices.pop_back();
|
// and allocate new proxies accordingly
|
||||||
_proxies[index].sphere = newSphere;
|
ProxyID maxID = _IDAllocator.getNumAllocatedIndices();
|
||||||
_proxies[index].region = Space::REGION_UNKNOWN;
|
if (maxID > (Index) _proxies.size()) {
|
||||||
_proxies[index].prevRegion = Space::REGION_UNKNOWN;
|
_proxies.resize(maxID + 100); // allocate the maxId and more
|
||||||
return index;
|
_owners.resize(maxID + 100);
|
||||||
|
}
|
||||||
|
// Now we know for sure that we have enough items in the array to
|
||||||
|
// capture anything coming from the transaction
|
||||||
|
|
||||||
|
processResets(transaction._resetItems);
|
||||||
|
processUpdates(transaction._updatedItems);
|
||||||
|
processRemoves(transaction._removedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Space::processResets(const Transaction::Resets& transactions) {
|
||||||
|
for (auto& reset : transactions) {
|
||||||
|
// Access the true item
|
||||||
|
auto proxyID = std::get<0>(reset);
|
||||||
|
auto& item = _proxies[proxyID];
|
||||||
|
|
||||||
|
// Reset the item with a new payload
|
||||||
|
item.sphere = (std::get<1>(reset));
|
||||||
|
item.prevRegion = item.region = Region::UNKNOWN;
|
||||||
|
|
||||||
|
_owners[proxyID] = (std::get<2>(reset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Space::deleteProxy(int32_t proxyId) {
|
void Space::processRemoves(const Transaction::Removes& transactions) {
|
||||||
if (proxyId >= (int32_t)_proxies.size() || _proxies.empty()) {
|
for (auto removedID : transactions) {
|
||||||
return;
|
_IDAllocator.freeIndex(removedID);
|
||||||
|
|
||||||
|
// Access the true item
|
||||||
|
auto& item = _proxies[removedID];
|
||||||
|
|
||||||
|
// Kill it
|
||||||
|
item.prevRegion = item.region = Region::INVALID;
|
||||||
|
_owners[removedID] = Owner();
|
||||||
}
|
}
|
||||||
if (proxyId == (int32_t)_proxies.size() - 1) {
|
}
|
||||||
// remove proxy on back
|
|
||||||
_proxies.pop_back();
|
void Space::processUpdates(const Transaction::Updates& transactions) {
|
||||||
if (!_freeIndices.empty()) {
|
for (auto& update : transactions) {
|
||||||
// remove any freeIndices on back
|
auto updateID = std::get<0>(update);
|
||||||
std::sort(_freeIndices.begin(), _freeIndices.end());
|
if (updateID == INVALID_PROXY_ID) {
|
||||||
while(!_freeIndices.empty() && _freeIndices.back() == (int32_t)_proxies.size() - 1) {
|
continue;
|
||||||
_freeIndices.pop_back();
|
|
||||||
_proxies.pop_back();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
_proxies[proxyId].region = Space::REGION_INVALID;
|
|
||||||
_freeIndices.push_back(proxyId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Space::updateProxy(int32_t proxyId, const Space::Sphere& newSphere) {
|
// Access the true item
|
||||||
if (proxyId >= (int32_t)_proxies.size()) {
|
auto& item = _proxies[updateID];
|
||||||
return;
|
|
||||||
}
|
|
||||||
_proxies[proxyId].sphere = newSphere;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Space::setViews(const std::vector<Space::View>& views) {
|
// Update the item
|
||||||
_views = views;
|
item.sphere = (std::get<1>(update));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Space::categorizeAndGetChanges(std::vector<Space::Change>& changes) {
|
void Space::categorizeAndGetChanges(std::vector<Space::Change>& changes) {
|
||||||
|
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||||
uint32_t numProxies = (uint32_t)_proxies.size();
|
uint32_t numProxies = (uint32_t)_proxies.size();
|
||||||
uint32_t numViews = (uint32_t)_views.size();
|
uint32_t numViews = (uint32_t)_views.size();
|
||||||
for (uint32_t i = 0; i < numProxies; ++i) {
|
for (uint32_t i = 0; i < numProxies; ++i) {
|
||||||
Proxy& proxy = _proxies[i];
|
Proxy& proxy = _proxies[i];
|
||||||
if (proxy.region < Space::REGION_INVALID) {
|
if (proxy.region < Region::INVALID) {
|
||||||
uint8_t region = Space::REGION_UNKNOWN;
|
glm::vec3 proxyCenter = glm::vec3(proxy.sphere);
|
||||||
|
float proxyRadius = proxy.sphere.w;
|
||||||
|
uint8_t region = Region::UNKNOWN;
|
||||||
for (uint32_t j = 0; j < numViews; ++j) {
|
for (uint32_t j = 0; j < numViews; ++j) {
|
||||||
float distance2 = glm::distance2(_views[j].center, glm::vec3(_proxies[i].sphere));
|
auto& view = _views[j];
|
||||||
for (uint8_t c = 0; c < region; ++c) {
|
// for each 'view' we need only increment 'k' below the current value of 'region'
|
||||||
float touchDistance = _views[j].radiuses[c] + _proxies[i].sphere.w;
|
for (uint8_t k = 0; k < region; ++k) {
|
||||||
if (distance2 < touchDistance * touchDistance) {
|
float touchDistance = proxyRadius + view.regions[k].w;
|
||||||
region = c;
|
if (distance2(proxyCenter, glm::vec3(view.regions[k])) < touchDistance * touchDistance) {
|
||||||
|
region = k;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,3 +112,41 @@ void Space::categorizeAndGetChanges(std::vector<Space::Change>& changes) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Space::copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const {
|
||||||
|
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||||
|
auto numCopied = std::min(numDestProxies, (uint32_t)_proxies.size());
|
||||||
|
memcpy(proxies, _proxies.data(), numCopied * sizeof(Proxy));
|
||||||
|
return numCopied;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Owner Space::getOwner(int32_t proxyID) const {
|
||||||
|
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||||
|
if (isAllocatedID(proxyID) && (proxyID < (Index)_proxies.size())) {
|
||||||
|
return _owners[proxyID];
|
||||||
|
}
|
||||||
|
return Owner();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Space::getRegion(int32_t proxyID) const {
|
||||||
|
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||||
|
if (isAllocatedID(proxyID) && (proxyID < (Index)_proxies.size())) {
|
||||||
|
return _proxies[proxyID].region;
|
||||||
|
}
|
||||||
|
return (uint8_t)Region::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Space::clear() {
|
||||||
|
std::unique_lock<std::mutex> lock(_proxiesMutex);
|
||||||
|
_IDAllocator.clear();
|
||||||
|
_proxies.clear();
|
||||||
|
_owners.clear();
|
||||||
|
_views.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Space::setViews(const Views& views) {
|
||||||
|
_views = views;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Space::copyViews(std::vector<View>& copy) const {
|
||||||
|
copy = _views;
|
||||||
|
}
|
||||||
|
|
|
@ -15,39 +15,17 @@
|
||||||
#ifndef hifi_workload_Space_h
|
#ifndef hifi_workload_Space_h
|
||||||
#define hifi_workload_Space_h
|
#define hifi_workload_Space_h
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "Transaction.h"
|
||||||
|
|
||||||
namespace workload {
|
namespace workload {
|
||||||
|
|
||||||
class Space {
|
class Space : public Collection {
|
||||||
public:
|
public:
|
||||||
static const uint8_t REGION_NEAR = 0;
|
using ProxyUpdate = std::pair<int32_t, Sphere>;
|
||||||
static const uint8_t REGION_MIDDLE = 1;
|
|
||||||
static const uint8_t REGION_FAR = 2;
|
|
||||||
static const uint8_t REGION_UNKNOWN = 3;
|
|
||||||
static const uint8_t REGION_INVALID = 4;
|
|
||||||
|
|
||||||
using Sphere = glm::vec4; // <x,y,z> = center, w = radius
|
|
||||||
|
|
||||||
class Proxy {
|
|
||||||
public:
|
|
||||||
Proxy(const Sphere& s) : sphere(s) {}
|
|
||||||
Sphere sphere;
|
|
||||||
uint8_t region { REGION_UNKNOWN };
|
|
||||||
uint8_t prevRegion { REGION_UNKNOWN };
|
|
||||||
};
|
|
||||||
|
|
||||||
class View {
|
|
||||||
public:
|
|
||||||
View(const glm::vec3& pos, float nearRadius, float midRadius, float farRadius) : center(pos) {
|
|
||||||
radiuses[0] = nearRadius;
|
|
||||||
radiuses[1] = midRadius;
|
|
||||||
radiuses[2] = farRadius;
|
|
||||||
}
|
|
||||||
glm::vec3 center { 0.0f, 0.0f, 0.0f };
|
|
||||||
float radiuses[3] { 0.0f, 0.0f, 0.0f };
|
|
||||||
};
|
|
||||||
|
|
||||||
class Change {
|
class Change {
|
||||||
public:
|
public:
|
||||||
|
@ -57,23 +35,43 @@ public:
|
||||||
uint8_t prevRegion { 0 };
|
uint8_t prevRegion { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
Space() {}
|
Space();
|
||||||
|
|
||||||
int32_t createProxy(const Sphere& sphere);
|
void setViews(const Views& views);
|
||||||
void deleteProxy(int32_t proxyId);
|
|
||||||
void updateProxy(int32_t proxyId, const Sphere& sphere);
|
|
||||||
void setViews(const std::vector<View>& views);
|
|
||||||
|
|
||||||
uint32_t getNumObjects() const { return (uint32_t)(_proxies.size() - _freeIndices.size()); }
|
uint32_t getNumViews() const { return (uint32_t)(_views.size()); }
|
||||||
|
void copyViews(std::vector<View>& copy) const;
|
||||||
|
|
||||||
|
uint32_t getNumObjects() const { return _IDAllocator.getNumLiveIndices(); } // (uint32_t)(_proxies.size() - _freeIndices.size()); }
|
||||||
|
uint32_t getNumAllocatedProxies() const { return (uint32_t)(_IDAllocator.getNumAllocatedIndices()); }
|
||||||
|
|
||||||
void categorizeAndGetChanges(std::vector<Change>& changes);
|
void categorizeAndGetChanges(std::vector<Change>& changes);
|
||||||
|
uint32_t copyProxyValues(Proxy* proxies, uint32_t numDestProxies) const;
|
||||||
|
|
||||||
|
const Owner getOwner(int32_t proxyID) const;
|
||||||
|
uint8_t getRegion(int32_t proxyID) const;
|
||||||
|
|
||||||
|
void clear();
|
||||||
private:
|
private:
|
||||||
std::vector<Proxy> _proxies;
|
|
||||||
std::vector<View> _views;
|
void processTransactionFrame(const Transaction& transaction) override;
|
||||||
std::vector<int32_t> _freeIndices;
|
void processResets(const Transaction::Resets& transactions);
|
||||||
|
void processRemoves(const Transaction::Removes& transactions);
|
||||||
|
void processUpdates(const Transaction::Updates& transactions);
|
||||||
|
|
||||||
|
// The database of proxies is protected for editing by a mutex
|
||||||
|
mutable std::mutex _proxiesMutex;
|
||||||
|
Proxy::Vector _proxies;
|
||||||
|
std::vector<Owner> _owners;
|
||||||
|
|
||||||
|
Views _views;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using SpacePointer = std::shared_ptr<Space>;
|
||||||
|
using Changes = std::vector<Space::Change>;
|
||||||
|
using IndexVectors = std::vector<IndexVector>;
|
||||||
|
using Timings = std::vector<std::chrono::nanoseconds>;
|
||||||
|
|
||||||
} // namespace workload
|
} // namespace workload
|
||||||
|
|
||||||
#endif // hifi_workload_Space_h
|
#endif // hifi_workload_Space_h
|
||||||
|
|
34
libraries/workload/src/workload/SpaceClassifier.cpp
Normal file
34
libraries/workload/src/workload/SpaceClassifier.cpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// SpaceClassifier.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.02.21
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
#include "SpaceClassifier.h"
|
||||||
|
|
||||||
|
#include "ViewTask.h"
|
||||||
|
#include "RegionState.h"
|
||||||
|
|
||||||
|
using namespace workload;
|
||||||
|
|
||||||
|
void PerformSpaceTransaction::configure(const Config& config) {
|
||||||
|
|
||||||
|
}
|
||||||
|
void PerformSpaceTransaction::run(const WorkloadContextPointer& context) {
|
||||||
|
context->_space->enqueueFrame();
|
||||||
|
context->_space->processTransactionQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpaceClassifierTask::build(JobModel& model, const Varying& in, Varying& out) {
|
||||||
|
model.addJob<AssignSpaceViews >("assignSpaceViews", in);
|
||||||
|
model.addJob<PerformSpaceTransaction>("updateSpace");
|
||||||
|
const auto regionTrackerOut = model.addJob<RegionTracker>("regionTracker");
|
||||||
|
const auto regionChanges = regionTrackerOut.getN<RegionTracker::Outputs>(1);
|
||||||
|
model.addJob<RegionState>("regionState", regionChanges);
|
||||||
|
out = regionTrackerOut;
|
||||||
|
}
|
||||||
|
|
47
libraries/workload/src/workload/SpaceClassifier.h
Normal file
47
libraries/workload/src/workload/SpaceClassifier.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// SpaceClassifier.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Andrew Meadows 2018.02.21
|
||||||
|
// 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_workload_SpaceClassifier_h
|
||||||
|
#define hifi_workload_SpaceClassifier_h
|
||||||
|
|
||||||
|
#include "ViewTask.h"
|
||||||
|
#include "RegionTracker.h"
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
class SpaceClassifierTask {
|
||||||
|
public:
|
||||||
|
using Inputs = Views;
|
||||||
|
using Outputs = RegionTracker::Outputs;
|
||||||
|
using JobModel = Task::ModelIO<SpaceClassifierTask, Inputs, Outputs>;
|
||||||
|
void build(JobModel& model, const Varying& in, Varying& out);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class PerformSpaceTransactionConfig : public Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
signals :
|
||||||
|
void dirty();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
class PerformSpaceTransaction {
|
||||||
|
public:
|
||||||
|
using Config = PerformSpaceTransactionConfig;
|
||||||
|
using JobModel = Job::Model<PerformSpaceTransaction, Config>;
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const WorkloadContextPointer& context);
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_SpaceClassifier_h
|
166
libraries/workload/src/workload/Transaction.cpp
Normal file
166
libraries/workload/src/workload/Transaction.cpp
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
//
|
||||||
|
// Transaction.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.12
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
#include "Transaction.h"
|
||||||
|
|
||||||
|
using namespace workload;
|
||||||
|
|
||||||
|
|
||||||
|
void Transaction::reset(ProxyID id, const ProxyPayload& payload, const Owner& owner) {
|
||||||
|
_resetItems.emplace_back(Reset{ id, payload, owner });
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::remove(ProxyID id) {
|
||||||
|
_removedItems.emplace_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::update(ProxyID id, const ProxyPayload& payload) {
|
||||||
|
_updatedItems.emplace_back(id, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::reserve(const std::vector<Transaction>& transactionContainer) {
|
||||||
|
size_t resetItemsCount = 0;
|
||||||
|
size_t removedItemsCount = 0;
|
||||||
|
size_t updatedItemsCount = 0;
|
||||||
|
|
||||||
|
for (const auto& transaction : transactionContainer) {
|
||||||
|
resetItemsCount += transaction._resetItems.size();
|
||||||
|
removedItemsCount += transaction._removedItems.size();
|
||||||
|
updatedItemsCount += transaction._updatedItems.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
_resetItems.reserve(resetItemsCount);
|
||||||
|
_removedItems.reserve(removedItemsCount);
|
||||||
|
_updatedItems.reserve(updatedItemsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::merge(const std::vector<Transaction>& transactionContainer) {
|
||||||
|
reserve(transactionContainer);
|
||||||
|
for (const auto& transaction : transactionContainer) {
|
||||||
|
merge(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Transaction::merge(std::vector<Transaction>&& transactionContainer) {
|
||||||
|
reserve(transactionContainer);
|
||||||
|
auto begin = std::make_move_iterator(transactionContainer.begin());
|
||||||
|
auto end = std::make_move_iterator(transactionContainer.end());
|
||||||
|
for (auto itr = begin; itr != end; ++itr) {
|
||||||
|
merge(*itr);
|
||||||
|
}
|
||||||
|
transactionContainer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void moveElements(T& target, T& source) {
|
||||||
|
target.insert(target.end(), std::make_move_iterator(source.begin()), std::make_move_iterator(source.end()));
|
||||||
|
source.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void copyElements(T& target, const T& source) {
|
||||||
|
target.insert(target.end(), source.begin(), source.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Transaction::reset(const Resets& resets) {
|
||||||
|
copyElements(_resetItems, resets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::remove(const Removes& removes) {
|
||||||
|
copyElements(_removedItems, removes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::update(const Updates& updates) {
|
||||||
|
copyElements(_updatedItems, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::merge(Transaction&& transaction) {
|
||||||
|
moveElements(_resetItems, transaction._resetItems);
|
||||||
|
moveElements(_removedItems, transaction._removedItems);
|
||||||
|
moveElements(_updatedItems, transaction._updatedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::merge(const Transaction& transaction) {
|
||||||
|
copyElements(_resetItems, transaction._resetItems);
|
||||||
|
copyElements(_removedItems, transaction._removedItems);
|
||||||
|
copyElements(_updatedItems, transaction._updatedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transaction::clear() {
|
||||||
|
_resetItems.clear();
|
||||||
|
_removedItems.clear();
|
||||||
|
_updatedItems.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Collection::Collection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection::~Collection() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ProxyID Collection::allocateID() {
|
||||||
|
// Just increment and return the previous value initialized at 0
|
||||||
|
return _IDAllocator.allocateIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Collection::isAllocatedID(const ProxyID& id) const {
|
||||||
|
return _IDAllocator.checkIndex(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enqueue change batch to the Collection
|
||||||
|
void Collection::enqueueTransaction(const Transaction& transaction) {
|
||||||
|
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
|
||||||
|
_transactionQueue.emplace_back(transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Collection::enqueueTransaction(Transaction&& transaction) {
|
||||||
|
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
|
||||||
|
_transactionQueue.emplace_back(std::move(transaction));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Collection::enqueueFrame() {
|
||||||
|
TransactionQueue localTransactionQueue;
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_transactionQueueMutex);
|
||||||
|
localTransactionQueue.swap(_transactionQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction consolidatedTransaction;
|
||||||
|
consolidatedTransaction.merge(std::move(localTransactionQueue));
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(_transactionFramesMutex);
|
||||||
|
_transactionFrames.push_back(consolidatedTransaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ++_transactionFrameNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Collection::processTransactionQueue() {
|
||||||
|
static TransactionFrames queuedFrames;
|
||||||
|
{
|
||||||
|
// capture the queued frames and clear the queue
|
||||||
|
std::unique_lock<std::mutex> lock(_transactionFramesMutex);
|
||||||
|
queuedFrames.swap(_transactionFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through the queue of frames and process them
|
||||||
|
for (auto& frame : queuedFrames) {
|
||||||
|
processTransactionFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedFrames.clear();
|
||||||
|
}
|
181
libraries/workload/src/workload/Transaction.h
Normal file
181
libraries/workload/src/workload/Transaction.h
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
//
|
||||||
|
// Transaction.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.12
|
||||||
|
// 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_workload_Transaction_h
|
||||||
|
#define hifi_workload_Transaction_h
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include "Proxy.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
|
||||||
|
namespace indexed_container {
|
||||||
|
|
||||||
|
using Index = int32_t;
|
||||||
|
const Index MAXIMUM_INDEX{ 1 << 30 };
|
||||||
|
const Index INVALID_INDEX{ -1 };
|
||||||
|
using Indices = std::vector< Index >;
|
||||||
|
|
||||||
|
template <Index MaxNumElements = MAXIMUM_INDEX>
|
||||||
|
class Allocator {
|
||||||
|
public:
|
||||||
|
Allocator() {}
|
||||||
|
Indices _freeIndices;
|
||||||
|
Index _nextNewIndex{ 0 };
|
||||||
|
|
||||||
|
bool checkIndex(Index index) const { return ((index >= 0) && (index < _nextNewIndex)); }
|
||||||
|
Index getNumLiveIndices() const { return _nextNewIndex - (Index)_freeIndices.size(); }
|
||||||
|
Index getNumFreeIndices() const { return (Index)_freeIndices.size(); }
|
||||||
|
Index getNumAllocatedIndices() const { return _nextNewIndex; }
|
||||||
|
|
||||||
|
Index allocateIndex() {
|
||||||
|
if (_freeIndices.empty()) {
|
||||||
|
Index index = _nextNewIndex;
|
||||||
|
if (index >= MaxNumElements) {
|
||||||
|
// abort! we are trying to go overboard with the total number of allocated elements
|
||||||
|
assert(false);
|
||||||
|
// This should never happen because Bricks are allocated along with the cells and there
|
||||||
|
// is already a cap on the cells allocation
|
||||||
|
return INVALID_INDEX;
|
||||||
|
}
|
||||||
|
_nextNewIndex++;
|
||||||
|
return index;
|
||||||
|
} else {
|
||||||
|
// TODO: sort _freeIndices when neccessary to help keep used allocated indices more tightly packed
|
||||||
|
Index index = _freeIndices.back();
|
||||||
|
_freeIndices.pop_back();
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeIndex(Index index) {
|
||||||
|
if (checkIndex(index)) {
|
||||||
|
_freeIndices.push_back(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_freeIndices.clear();
|
||||||
|
_nextNewIndex = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
using Index = indexed_container::Index;
|
||||||
|
using IndexVector = indexed_container::Indices;
|
||||||
|
|
||||||
|
using ProxyID = Index;
|
||||||
|
const ProxyID INVALID_PROXY_ID{ indexed_container ::INVALID_INDEX };
|
||||||
|
|
||||||
|
// Transaction is the mechanism to make any change to the Space.
|
||||||
|
// Whenever a new proxy need to be reset,
|
||||||
|
// or when an proxy changes its position or its size
|
||||||
|
// or when an proxy is destroyed
|
||||||
|
// These changes must be expressed through the corresponding command from the Transaction
|
||||||
|
// The Transaction is then queued on the Space so all the pending transactions can be consolidated and processed at the time
|
||||||
|
// of updating the space at the Frame boundary.
|
||||||
|
//
|
||||||
|
class Transaction {
|
||||||
|
friend class Space;
|
||||||
|
public:
|
||||||
|
using ProxyPayload = Sphere;
|
||||||
|
|
||||||
|
using Reset = std::tuple<ProxyID, ProxyPayload, Owner>;
|
||||||
|
using Remove = ProxyID;
|
||||||
|
using Update = std::tuple<ProxyID, ProxyPayload>;
|
||||||
|
|
||||||
|
using Resets = std::vector<Reset>;
|
||||||
|
using Removes = std::vector<Remove>;
|
||||||
|
using Updates = std::vector<Update>;
|
||||||
|
|
||||||
|
Transaction() {}
|
||||||
|
~Transaction() {}
|
||||||
|
|
||||||
|
// Proxy transactions
|
||||||
|
void reset(ProxyID id, const ProxyPayload& sphere, const Owner& owner);
|
||||||
|
void reset(const Resets& resets);
|
||||||
|
void remove(ProxyID id);
|
||||||
|
void remove(const Removes& removes);
|
||||||
|
bool hasRemovals() const { return !_removedItems.empty(); }
|
||||||
|
|
||||||
|
void update(ProxyID id, const ProxyPayload& sphere);
|
||||||
|
void update(const Updates& updates);
|
||||||
|
|
||||||
|
void reserve(const std::vector<Transaction>& transactionContainer);
|
||||||
|
void merge(const std::vector<Transaction>& transactionContainer);
|
||||||
|
void merge(std::vector<Transaction>&& transactionContainer);
|
||||||
|
void merge(const Transaction& transaction);
|
||||||
|
void merge(Transaction&& transaction);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
|
||||||
|
Resets _resetItems;
|
||||||
|
Removes _removedItems;
|
||||||
|
Updates _updatedItems;
|
||||||
|
};
|
||||||
|
typedef std::vector<Transaction> TransactionQueue;
|
||||||
|
|
||||||
|
class Collection {
|
||||||
|
public:
|
||||||
|
Collection();
|
||||||
|
~Collection();
|
||||||
|
|
||||||
|
// This call is thread safe, can be called from anywhere to allocate a new ID
|
||||||
|
ProxyID allocateID();
|
||||||
|
|
||||||
|
// Check that the ID is valid and allocated for this collection, this a threadsafe call
|
||||||
|
bool isAllocatedID(const ProxyID& id) const;
|
||||||
|
|
||||||
|
// THis is the total number of allocated proxies, this a threadsafe call
|
||||||
|
Index getNumAllocatedProxies() const { return _IDAllocator.getNumAllocatedIndices(); }
|
||||||
|
|
||||||
|
// Enqueue transaction to the collection
|
||||||
|
void enqueueTransaction(const Transaction& transaction);
|
||||||
|
|
||||||
|
// Enqueue transaction to the collection
|
||||||
|
void enqueueTransaction(Transaction&& transaction);
|
||||||
|
|
||||||
|
// Enqueue end of frame transactions boundary
|
||||||
|
uint32_t enqueueFrame();
|
||||||
|
|
||||||
|
// Process the pending transactions queued
|
||||||
|
virtual void processTransactionQueue();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// Thread safe elements that can be accessed from anywhere
|
||||||
|
indexed_container::Allocator<> _IDAllocator;
|
||||||
|
|
||||||
|
std::mutex _transactionQueueMutex;
|
||||||
|
TransactionQueue _transactionQueue;
|
||||||
|
|
||||||
|
|
||||||
|
std::mutex _transactionFramesMutex;
|
||||||
|
using TransactionFrames = std::vector<Transaction>;
|
||||||
|
TransactionFrames _transactionFrames;
|
||||||
|
uint32_t _transactionFrameNumber{ 0 };
|
||||||
|
|
||||||
|
// Process one transaction frame
|
||||||
|
virtual void processTransactionFrame(const Transaction& transaction) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_Transaction_h
|
69
libraries/workload/src/workload/View.cpp
Normal file
69
libraries/workload/src/workload/View.cpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
// View.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
#include "View.h"
|
||||||
|
#include <ViewFrustum.h>
|
||||||
|
|
||||||
|
using namespace workload;
|
||||||
|
|
||||||
|
void View::setFov(float angleRad) {
|
||||||
|
float halfAngle = angleRad * 0.5f;
|
||||||
|
|
||||||
|
fov_halfAngle_tan_cos_sin.x = halfAngle;
|
||||||
|
fov_halfAngle_tan_cos_sin.y = tanf(halfAngle);
|
||||||
|
fov_halfAngle_tan_cos_sin.z = cosf(halfAngle);
|
||||||
|
fov_halfAngle_tan_cos_sin.w = sinf(halfAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::makeHorizontal() {
|
||||||
|
direction = glm::normalize(glm::vec3(direction.x, 0.0f, direction.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
View View::evalFromFrustum(const ViewFrustum& frustum, const glm::vec3& offset) {
|
||||||
|
View view;
|
||||||
|
view.origin = frustum.getPosition() + offset;
|
||||||
|
view.direction = frustum.getDirection();
|
||||||
|
view.setFov(frustum.getFieldOfView());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sphere View::evalRegionSphere(const View& view, float originRadius, float maxDistance) {
|
||||||
|
float radius = (maxDistance + originRadius) / 2.0f;
|
||||||
|
float center = radius - originRadius;
|
||||||
|
return Sphere(view.origin + view.direction * center, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::updateRegionsDefault(View& view) {
|
||||||
|
std::vector<float> config(Region::NUM_VIEW_REGIONS * 2, 0.0f);
|
||||||
|
|
||||||
|
float refFar = 10.0f;
|
||||||
|
float refClose = 2.0f;
|
||||||
|
for (int i = 0; i < Region::NUM_VIEW_REGIONS; i++) {
|
||||||
|
float weight = i + 1.0f;
|
||||||
|
config[i * 2] = refClose;
|
||||||
|
config[i * 2 + 1] = refFar * weight;
|
||||||
|
refFar *= 2.0f;
|
||||||
|
}
|
||||||
|
updateRegionsFromBackFrontDistances(view, config.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::updateRegionsFromBackFronts(View& view) {
|
||||||
|
for (int i = 0; i < Region::NUM_VIEW_REGIONS; i++) {
|
||||||
|
view.regions[i] = evalRegionSphere(view, view.regionBackFronts[i].x, view.regionBackFronts[i].y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::updateRegionsFromBackFrontDistances(View& view, const float* configDistances) {
|
||||||
|
for (int i = 0; i < Region::NUM_VIEW_REGIONS; i++) {
|
||||||
|
view.regionBackFronts[i] = glm::vec2(configDistances[i * 2], configDistances[i * 2 + 1]);
|
||||||
|
}
|
||||||
|
updateRegionsFromBackFronts(view);
|
||||||
|
}
|
74
libraries/workload/src/workload/View.h
Normal file
74
libraries/workload/src/workload/View.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// View.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.05
|
||||||
|
// 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_workload_View_h
|
||||||
|
#define hifi_workload_View_h
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/constants.hpp>
|
||||||
|
|
||||||
|
#include "Region.h"
|
||||||
|
|
||||||
|
class ViewFrustum;
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
|
||||||
|
using Sphere = glm::vec4;
|
||||||
|
|
||||||
|
class View {
|
||||||
|
public:
|
||||||
|
View() = default;
|
||||||
|
View(const View& view) = default;
|
||||||
|
|
||||||
|
// View attributes:
|
||||||
|
|
||||||
|
// direction
|
||||||
|
glm::vec3 direction{ 0.0f, 0.0f, -1.0f };
|
||||||
|
|
||||||
|
// Max radius
|
||||||
|
float maxRadius{ FLT_MAX };
|
||||||
|
|
||||||
|
// Fov stores the half field of view angle, and tan/cos/sin ready to go, default is fov of 90deg
|
||||||
|
glm::vec4 fov_halfAngle_tan_cos_sin { glm::quarter_pi<float>(), 1.0f, glm::root_two<float>() * 0.5f, glm::root_two<float>() * 0.5f};
|
||||||
|
|
||||||
|
// Origin position
|
||||||
|
glm::vec3 origin{ 0.0f };
|
||||||
|
|
||||||
|
// Origin radius
|
||||||
|
float originRadius{ 0.5f };
|
||||||
|
|
||||||
|
// N regions distances
|
||||||
|
glm::vec2 regionBackFronts[Region::NUM_VIEW_REGIONS + 1];
|
||||||
|
|
||||||
|
// N regions spheres
|
||||||
|
Sphere regions[Region::NUM_VIEW_REGIONS];
|
||||||
|
|
||||||
|
// Set fov properties from angle
|
||||||
|
void setFov(float angleRad);
|
||||||
|
|
||||||
|
// Helper function to force the direction in the XZ plane
|
||||||
|
void makeHorizontal();
|
||||||
|
|
||||||
|
static View evalFromFrustum(const ViewFrustum& frustum, const glm::vec3& offset = glm::vec3());
|
||||||
|
static Sphere evalRegionSphere(const View& view, float originRadius, float maxDistance);
|
||||||
|
|
||||||
|
static void updateRegionsDefault(View& view);
|
||||||
|
static void updateRegionsFromBackFronts(View& view);
|
||||||
|
static void updateRegionsFromBackFrontDistances(View& view, const float* configDistances);
|
||||||
|
};
|
||||||
|
|
||||||
|
using Views = std::vector<View>;
|
||||||
|
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_View_h
|
160
libraries/workload/src/workload/ViewTask.cpp
Normal file
160
libraries/workload/src/workload/ViewTask.cpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
//
|
||||||
|
// ViewTask.cpp
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.05
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
#include "ViewTask.h"
|
||||||
|
|
||||||
|
using namespace workload;
|
||||||
|
|
||||||
|
|
||||||
|
void AssignSpaceViews::run(const WorkloadContextPointer& renderContext, const Input& inputs) {
|
||||||
|
// Just do what it says
|
||||||
|
renderContext->_space->setViews(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupViews::configure(const Config& config) {
|
||||||
|
data = config.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupViews::run(const WorkloadContextPointer& renderContext, const Input& inputs, Output& outputs) {
|
||||||
|
// If views are frozen don't use the input
|
||||||
|
if (!data.freezeViews) {
|
||||||
|
_views = inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& outViews = outputs;
|
||||||
|
outViews.clear();
|
||||||
|
|
||||||
|
// Filter the first view centerer on the avatar head if needed
|
||||||
|
if (_views.size() >= 2) {
|
||||||
|
if (data.useAvatarView) {
|
||||||
|
outViews.push_back(_views[0]);
|
||||||
|
outViews.insert(outViews.end(), _views.begin() + 2, _views.end());
|
||||||
|
} else {
|
||||||
|
outViews.insert(outViews.end(), _views.begin() + 1, _views.end());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outViews = _views;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force frutum orientation horizontal if needed
|
||||||
|
if (outViews.size() > 0 && data.forceViewHorizontal) {
|
||||||
|
outViews[0].makeHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force frutum orientation horizontal if needed
|
||||||
|
if (outViews.size() > 0 && data.simulateSecondaryCamera) {
|
||||||
|
auto view = outViews[0];
|
||||||
|
auto secondaryDirectionFlat = glm::normalize(glm::vec3(view.direction.x, 0.0f, view.direction.z));
|
||||||
|
auto secondaryDirection = glm::normalize(glm::vec3(secondaryDirectionFlat.z, 0.0f, -secondaryDirectionFlat.x));
|
||||||
|
|
||||||
|
view.origin += -20.0f * secondaryDirection;
|
||||||
|
view.direction = -secondaryDirection;
|
||||||
|
|
||||||
|
outViews.insert(outViews.begin() + 1, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update regions based on the current config
|
||||||
|
for (auto& v : outViews) {
|
||||||
|
View::updateRegionsFromBackFrontDistances(v, (float*) &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// outViews is ready to be used
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ControlViews::ControlViews() {
|
||||||
|
for (int32_t i = 0; i < workload::Region::NUM_VIEW_REGIONS; i++) {
|
||||||
|
regionBackFronts[i] = MIN_VIEW_BACK_FRONTS[i];
|
||||||
|
regionRegulators[i] = Regulator(std::chrono::milliseconds(2), MIN_VIEW_BACK_FRONTS[i], MAX_VIEW_BACK_FRONTS[i], glm::vec2(RELATIVE_STEP_DOWN), glm::vec2(RELATIVE_STEP_UP));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlViews::configure(const Config& config) {
|
||||||
|
_data = config.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlViews::run(const workload::WorkloadContextPointer& runContext, const Input& inputs, Output& outputs) {
|
||||||
|
const auto& inViews = inputs.get0();
|
||||||
|
const auto& inTimings = inputs.get1();
|
||||||
|
auto& outViews = outputs;
|
||||||
|
outViews.clear();
|
||||||
|
outViews = inViews;
|
||||||
|
|
||||||
|
if (_data.regulateViewRanges && inTimings.size()) {
|
||||||
|
regulateViews(outViews, inTimings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export the timings for debuging
|
||||||
|
if (inTimings.size()) {
|
||||||
|
_dataExport.timings[workload::Region::R1] = std::chrono::duration<float, std::milli>(inTimings[0]).count();
|
||||||
|
_dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1];
|
||||||
|
_dataExport.timings[workload::Region::R3] = std::chrono::duration<float, std::milli>(inTimings[1]).count();
|
||||||
|
|
||||||
|
auto config = std::static_pointer_cast<Config>(runContext->jobConfig);
|
||||||
|
config->dataExport = _dataExport;
|
||||||
|
config->emitDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 Regulator::run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current) {
|
||||||
|
// Regulate next value based on current moving toward the goal budget
|
||||||
|
float error_ms = std::chrono::duration<float, std::milli>(_budget - measured).count();
|
||||||
|
float coef = glm::clamp(error_ms / std::chrono::duration<float, std::milli>(regulationDuration).count(), -1.0f, 1.0f);
|
||||||
|
return current * (1.0f + coef * (error_ms < 0.0f ? _relativeStepDown : _relativeStepUp));
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 Regulator::clamp(const glm::vec2& backFront) const {
|
||||||
|
// Clamp to min max
|
||||||
|
return glm::clamp(backFront, _minRange, _maxRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlViews::regulateViews(workload::Views& outViews, const workload::Timings& timings) {
|
||||||
|
|
||||||
|
for (auto& outView : outViews) {
|
||||||
|
for (int32_t r = 0; r < workload::Region::NUM_VIEW_REGIONS; r++) {
|
||||||
|
outView.regionBackFronts[r] = regionBackFronts[r];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loopDuration = std::chrono::nanoseconds{ std::chrono::milliseconds(16) };
|
||||||
|
regionBackFronts[workload::Region::R1] = regionRegulators[workload::Region::R1].run(loopDuration, timings[0], regionBackFronts[workload::Region::R1]);
|
||||||
|
regionBackFronts[workload::Region::R2] = regionRegulators[workload::Region::R2].run(loopDuration, timings[0], regionBackFronts[workload::Region::R2]);
|
||||||
|
regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]);
|
||||||
|
|
||||||
|
enforceRegionContainment();
|
||||||
|
|
||||||
|
_dataExport.ranges[workload::Region::R1] = regionBackFronts[workload::Region::R1];
|
||||||
|
_dataExport.ranges[workload::Region::R2] = regionBackFronts[workload::Region::R2];
|
||||||
|
_dataExport.ranges[workload::Region::R3] = regionBackFronts[workload::Region::R3];
|
||||||
|
|
||||||
|
for (auto& outView : outViews) {
|
||||||
|
outView.regionBackFronts[workload::Region::R1] = regionBackFronts[workload::Region::R1];
|
||||||
|
outView.regionBackFronts[workload::Region::R2] = regionBackFronts[workload::Region::R2];
|
||||||
|
outView.regionBackFronts[workload::Region::R3] = regionBackFronts[workload::Region::R3];
|
||||||
|
|
||||||
|
workload::View::updateRegionsFromBackFronts(outView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ControlViews::enforceRegionContainment() {
|
||||||
|
// inner regions should never overreach outer
|
||||||
|
// and each region should never exceed its min/max limits
|
||||||
|
const glm::vec2 MIN_REGION_GAP = { 1.0f, 2.0f };
|
||||||
|
// enforce outside --> in
|
||||||
|
for (int32_t i = workload::Region::NUM_VIEW_REGIONS - 2; i >= 0; --i) {
|
||||||
|
int32_t j = i + 1;
|
||||||
|
regionBackFronts[i] = regionRegulators[i].clamp(glm::min(regionBackFronts[i], regionBackFronts[j] - MIN_REGION_GAP));
|
||||||
|
}
|
||||||
|
// enforce inside --> out
|
||||||
|
for (int32_t i = 1; i < workload::Region::NUM_VIEW_REGIONS; ++i) {
|
||||||
|
int32_t j = i - 1;
|
||||||
|
regionBackFronts[i] = regionRegulators[i].clamp(glm::max(regionBackFronts[i], regionBackFronts[j] + MIN_REGION_GAP));
|
||||||
|
}
|
||||||
|
}
|
257
libraries/workload/src/workload/ViewTask.h
Normal file
257
libraries/workload/src/workload/ViewTask.h
Normal file
|
@ -0,0 +1,257 @@
|
||||||
|
//
|
||||||
|
// ViewTask.h
|
||||||
|
// libraries/workload/src/workload
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau 2018.03.05
|
||||||
|
// 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_workload_ViewTask_h
|
||||||
|
#define hifi_workload_ViewTask_h
|
||||||
|
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
QVariantList toVariantList(const QList<T> &list)
|
||||||
|
{
|
||||||
|
QVariantList newList;
|
||||||
|
foreach(const T &item, list)
|
||||||
|
newList << item;
|
||||||
|
|
||||||
|
return newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace workload {
|
||||||
|
|
||||||
|
const std::vector<glm::vec2> MIN_VIEW_BACK_FRONTS = {
|
||||||
|
{ 3.0f, 4.0f },
|
||||||
|
{ 6.0f, 8.0f },
|
||||||
|
{ 9.0f, 12.0f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<glm::vec2> MAX_VIEW_BACK_FRONTS = {
|
||||||
|
{ 100.0f, 200.0f },
|
||||||
|
{ 150.0f, 300.0f },
|
||||||
|
{ 250.0f, 500.0f }
|
||||||
|
};
|
||||||
|
|
||||||
|
const float RELATIVE_STEP_DOWN = 0.05f;
|
||||||
|
const float RELATIVE_STEP_UP = 0.04f;
|
||||||
|
|
||||||
|
class SetupViewsConfig : public Job::Config{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(float r1Front READ getR1Front WRITE setR1Front NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r1Back READ getR1Back WRITE setR1Back NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2Front READ getR2Front WRITE setR2Front NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2Back READ getR2Back WRITE setR2Back NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3Front READ getR3Front WRITE setR3Front NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3Back READ getR3Back WRITE setR3Back NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool freezeViews READ getFreezeView WRITE setFreezeView NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool useAvatarView READ useAvatarView WRITE setUseAvatarView NOTIFY dirty)
|
||||||
|
Q_PROPERTY(bool forceViewHorizontal READ forceViewHorizontal WRITE setForceViewHorizontal NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool simulateSecondaryCamera READ simulateSecondaryCamera WRITE setSimulateSecondaryCamera NOTIFY dirty)
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
float getR1Front() const { return data.r1Front; }
|
||||||
|
float getR1Back() const { return data.r1Back; }
|
||||||
|
float getR2Front() const { return data.r2Front; }
|
||||||
|
float getR2Back() const { return data.r2Back; }
|
||||||
|
float getR3Front() const { return data.r3Front; }
|
||||||
|
float getR3Back() const { return data.r3Back; }
|
||||||
|
|
||||||
|
void setR1Front(float d) { data.r1Front = d; emit dirty(); }
|
||||||
|
void setR1Back(float d) { data.r1Back = d; emit dirty(); }
|
||||||
|
void setR2Front(float d) { data.r2Front = d; emit dirty(); }
|
||||||
|
void setR2Back(float d) { data.r2Back = d; emit dirty(); }
|
||||||
|
void setR3Front(float d) { data.r3Front = d; emit dirty(); }
|
||||||
|
void setR3Back(float d) { data.r3Back = d; emit dirty(); }
|
||||||
|
|
||||||
|
bool getFreezeView() const { return data.freezeViews; }
|
||||||
|
void setFreezeView(bool freeze) { data.freezeViews = freeze; emit dirty(); }
|
||||||
|
bool useAvatarView() const { return data.useAvatarView; }
|
||||||
|
void setUseAvatarView(bool use) { data.useAvatarView = use; emit dirty(); }
|
||||||
|
bool forceViewHorizontal() const { return data.forceViewHorizontal; }
|
||||||
|
void setForceViewHorizontal(bool use) { data.forceViewHorizontal = use; emit dirty(); }
|
||||||
|
|
||||||
|
bool simulateSecondaryCamera() const { return data.simulateSecondaryCamera; }
|
||||||
|
void setSimulateSecondaryCamera(bool use) { data.simulateSecondaryCamera = use; emit dirty(); }
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
float r1Back { MAX_VIEW_BACK_FRONTS[0].x };
|
||||||
|
float r1Front { MAX_VIEW_BACK_FRONTS[0].y };
|
||||||
|
|
||||||
|
float r2Back{ MAX_VIEW_BACK_FRONTS[1].x };
|
||||||
|
float r2Front{ MAX_VIEW_BACK_FRONTS[1].y };
|
||||||
|
|
||||||
|
float r3Back{ MAX_VIEW_BACK_FRONTS[2].x };
|
||||||
|
float r3Front{ MAX_VIEW_BACK_FRONTS[2].y };
|
||||||
|
|
||||||
|
bool freezeViews{ false };
|
||||||
|
bool useAvatarView{ false };
|
||||||
|
bool forceViewHorizontal{ false };
|
||||||
|
bool simulateSecondaryCamera{ false };
|
||||||
|
} data;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetupViews {
|
||||||
|
public:
|
||||||
|
using Config = SetupViewsConfig;
|
||||||
|
using Input = Views;
|
||||||
|
using Output = Views;
|
||||||
|
using JobModel = Job::ModelIO<SetupViews, Input, Output, Config>;
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const workload::WorkloadContextPointer& renderContext, const Input& inputs, Output& outputs);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Config::Data data;
|
||||||
|
Views _views;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AssignSpaceViews {
|
||||||
|
public:
|
||||||
|
using Input = Views;
|
||||||
|
using JobModel = Job::ModelI<AssignSpaceViews, Input>;
|
||||||
|
|
||||||
|
void run(const workload::WorkloadContextPointer& renderContext, const Input& inputs);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ControlViewsConfig : public workload::Job::Config {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool regulateViewRanges READ regulateViewRanges WRITE setRegulateViewRanges NOTIFY dirty)
|
||||||
|
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1Timing READ r1Timing NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2Timing READ r2Timing NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3Timing READ r3Timing NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1RangeBack READ r1RangeBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2RangeBack READ r2RangeBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3RangeBack READ r3RangeBack NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1RangeFront READ r1RangeFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2RangeFront READ r2RangeFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3RangeFront READ r3RangeFront NOTIFY dirty)
|
||||||
|
/*
|
||||||
|
Q_PROPERTY(float r1MinRangeBack READ r1MinRangeBack WRITE setR1MinRangeBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2MinRangeBack READ r2MinRangeBack WRITE setR2MinRangeBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3MinRangeBack READ r3MinRangeBack WRITE setR3MinRangeBack NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1MinRangeFront READ r1MinRangeFront WRITE setR1MinRangeFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2MinRangeFront READ r2MinRangeFront WRITE setR2MinRangeFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3MinRangeFront READ r3MinRangeFront WRITE setR3MinRangeFront NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1MaxRangeBack READ r1MaxRangeBack WRITE setR1MaxRangeBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2MaxRangeBack READ r2MaxRangeBack WRITE setR2MaxRangeBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3MaxRangeBack READ r3MaxRangeBack WRITE setR3MaxRangeBack NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1MaxRangeFront READ r1MaxRangeFront WRITE setR1MaxRangeFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2MaxRangeFront READ r2MaxRangeFront WRITE setR2MaxRangeFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3MaxRangeFront READ r3MaxRangeFront WRITE setR3MaxRangeFront NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1SpeedDownBack READ r1SpeedDownBack WRITE setR1SpeedDownBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2SpeedDownBack READ r2SpeedDownBack WRITE setR2SpeedDownBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3SpeedDownBack READ r3SpeedDownBack WRITE setR3SpeedDownBack NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1SpeedDownFront READ r1SpeedDownFront WRITE setR1SpeedDownFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2SpeedDownFront READ r2SpeedDownFront WRITE setR2SpeedDownFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3SpeedDownFront READ r3SpeedDownFront WRITE setR3SpeedDownFront NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1SpeedUpBack READ r1SpeedUpBack WRITE setR1SpeedUpBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2SpeedUpBack READ r2SpeedUpBack WRITE setR2SpeedUpBack NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3SpeedUpBack READ r3SpeedUpBack WRITE setR3SpeedUpBack NOTIFY dirty)
|
||||||
|
|
||||||
|
Q_PROPERTY(float r1SpeedUpFront READ r1SpeedUpFront WRITE setR1SpeedUpFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r2SpeedUpFront READ r2SpeedUpFront WRITE setR2SpeedUpFront NOTIFY dirty)
|
||||||
|
Q_PROPERTY(float r3SpeedUpFront READ r3SpeedUpFront WRITE setR3SpeedUpFront NOTIFY dirty)*/
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool regulateViewRanges() const { return data.regulateViewRanges; }
|
||||||
|
void setRegulateViewRanges(bool use) { data.regulateViewRanges = use; emit dirty(); }
|
||||||
|
|
||||||
|
float r1Timing() const { return dataExport.timings[workload::Region::R1]; }
|
||||||
|
float r2Timing() const { return dataExport.timings[workload::Region::R2]; }
|
||||||
|
float r3Timing() const { return dataExport.timings[workload::Region::R3]; }
|
||||||
|
|
||||||
|
float r1RangeBack() const { return dataExport.ranges[workload::Region::R1].x; }
|
||||||
|
float r2RangeBack() const { return dataExport.ranges[workload::Region::R2].x; }
|
||||||
|
float r3RangeBack() const { return dataExport.ranges[workload::Region::R3].x; }
|
||||||
|
|
||||||
|
float r1RangeFront() const { return dataExport.ranges[workload::Region::R1].y; }
|
||||||
|
float r2RangeFront() const { return dataExport.ranges[workload::Region::R2].y; }
|
||||||
|
float r3RangeFront() const { return dataExport.ranges[workload::Region::R3].y; }
|
||||||
|
|
||||||
|
|
||||||
|
struct Data {
|
||||||
|
bool regulateViewRanges{ false };
|
||||||
|
} data;
|
||||||
|
|
||||||
|
struct DataExport {
|
||||||
|
static const int SIZE{ workload::Region::NUM_VIEW_REGIONS };
|
||||||
|
float timings[SIZE];
|
||||||
|
glm::vec2 ranges[SIZE];
|
||||||
|
QList<qreal> _timings { 6, 2.0 };
|
||||||
|
|
||||||
|
} dataExport;
|
||||||
|
|
||||||
|
void emitDirty() { emit dirty(); }
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
Q_INVOKABLE QVariantList getTimings() const { return toVariantList(dataExport._timings); }
|
||||||
|
signals:
|
||||||
|
void dirty();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Regulator {
|
||||||
|
using Timing_ns = std::chrono::nanoseconds;
|
||||||
|
Timing_ns _budget{ std::chrono::milliseconds(2) };
|
||||||
|
glm::vec2 _minRange{ MIN_VIEW_BACK_FRONTS[0] };
|
||||||
|
glm::vec2 _maxRange{ MAX_VIEW_BACK_FRONTS[0] };
|
||||||
|
|
||||||
|
glm::vec2 _relativeStepDown{ RELATIVE_STEP_DOWN };
|
||||||
|
glm::vec2 _relativeStepUp{ RELATIVE_STEP_UP };
|
||||||
|
|
||||||
|
|
||||||
|
Regulator() {}
|
||||||
|
Regulator(const Timing_ns& budget_ns, const glm::vec2& minRange, const glm::vec2& maxRange, const glm::vec2& relativeStepDown, const glm::vec2& relativeStepUp) :
|
||||||
|
_budget(budget_ns), _minRange(minRange), _maxRange(maxRange), _relativeStepDown(relativeStepDown), _relativeStepUp(relativeStepUp) {}
|
||||||
|
|
||||||
|
glm::vec2 run(const Timing_ns& regulationDuration, const Timing_ns& measured, const glm::vec2& current);
|
||||||
|
glm::vec2 clamp(const glm::vec2& backFront) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ControlViews {
|
||||||
|
public:
|
||||||
|
using Config = ControlViewsConfig;
|
||||||
|
using Input = workload::VaryingSet2<workload::Views, workload::Timings>;
|
||||||
|
using Output = workload::Views;
|
||||||
|
using JobModel = workload::Job::ModelIO<ControlViews, Input, Output, Config>;
|
||||||
|
|
||||||
|
ControlViews();
|
||||||
|
|
||||||
|
void configure(const Config& config);
|
||||||
|
void run(const workload::WorkloadContextPointer& runContext, const Input& inputs, Output& outputs);
|
||||||
|
|
||||||
|
std::array<glm::vec2, workload::Region::NUM_VIEW_REGIONS> regionBackFronts;
|
||||||
|
std::array<Regulator, workload::Region::NUM_VIEW_REGIONS> regionRegulators;
|
||||||
|
|
||||||
|
void regulateViews(workload::Views& views, const workload::Timings& timings);
|
||||||
|
void enforceRegionContainment();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Config::Data _data;
|
||||||
|
Config::DataExport _dataExport;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace workload
|
||||||
|
|
||||||
|
#endif // hifi_workload_ViewTask_h
|
|
@ -13,7 +13,7 @@
|
||||||
// traverse task tree
|
// traverse task tree
|
||||||
function task_traverse(root, functor, depth) {
|
function task_traverse(root, functor, depth) {
|
||||||
if (root.isTask()) {
|
if (root.isTask()) {
|
||||||
depth++;
|
depth++;
|
||||||
for (var i = 0; i <root.getNumSubs(); i++) {
|
for (var i = 0; i <root.getNumSubs(); i++) {
|
||||||
var sub = root.getSubConfig(i);
|
var sub = root.getSubConfig(i);
|
||||||
if (functor(sub, depth, i)) {
|
if (functor(sub, depth, i)) {
|
||||||
|
|
|
@ -21,8 +21,6 @@ Rectangle {
|
||||||
HifiConstants { id: hifi;}
|
HifiConstants { id: hifi;}
|
||||||
color: hifi.colors.baseGray;
|
color: hifi.colors.baseGray;
|
||||||
id: root
|
id: root
|
||||||
// width: parent ? parent.width : 200
|
|
||||||
// height: parent ? parent.height : 400
|
|
||||||
property var rootConfig : Workload
|
property var rootConfig : Workload
|
||||||
|
|
||||||
Original.TextArea {
|
Original.TextArea {
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
TaskList 1.0 TaskList.qml
|
TaskList 1.0 TaskList.qml
|
||||||
TaskViewList 1.0 TaskViewList.qml
|
TaskViewList 1.0 TaskViewList.qml
|
||||||
|
|
|
@ -14,6 +14,7 @@ import QtQuick.Layouts 1.3
|
||||||
import "qrc:///qml/styles-uit"
|
import "qrc:///qml/styles-uit"
|
||||||
import "qrc:///qml/controls-uit" as HifiControls
|
import "qrc:///qml/controls-uit" as HifiControls
|
||||||
import "configSlider"
|
import "configSlider"
|
||||||
|
import "../lib/jet/qml" as Jet
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
HifiConstants { id: hifi;}
|
HifiConstants { id: hifi;}
|
||||||
|
|
23
scripts/developer/utilities/workload/inspectEngine.js
Normal file
23
scripts/developer/utilities/workload/inspectEngine.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
(function() { // BEGIN LOCAL_SCOPE
|
||||||
|
var qml = Script.resolvePath('./workloadInspector.qml');
|
||||||
|
var window = new OverlayWindow({
|
||||||
|
title: 'Inspect Engine',
|
||||||
|
source: qml,
|
||||||
|
width: 400,
|
||||||
|
height: 600
|
||||||
|
});
|
||||||
|
|
||||||
|
window.closed.connect(function () { Script.stop(); });
|
||||||
|
Script.scriptEnding.connect(function () {
|
||||||
|
/* var geometry = JSON.stringify({
|
||||||
|
x: window.position.x,
|
||||||
|
y: window.position.y,
|
||||||
|
width: window.size.x,
|
||||||
|
height: window.size.y
|
||||||
|
})
|
||||||
|
|
||||||
|
Settings.setValue(HMD_DEBUG_WINDOW_GEOMETRY_KEY, geometry);*/
|
||||||
|
window.close();
|
||||||
|
})
|
||||||
|
|
||||||
|
}()); // END LOCAL_SCOPE
|
263
scripts/developer/utilities/workload/test_physics_scene.js
Normal file
263
scripts/developer/utilities/workload/test_physics_scene.js
Normal file
|
@ -0,0 +1,263 @@
|
||||||
|
|
||||||
|
var DEFAULT_LIFETIME = 120;
|
||||||
|
var GRID_WORLD_SIZE = 100.0;
|
||||||
|
var GRID_WORLD_MARGIN = 5.0;
|
||||||
|
var GRID_WORLD_RESOLUTION = 30.0;
|
||||||
|
var GRID_WORLD_DROP_HEIGHT = 4.0;
|
||||||
|
|
||||||
|
var GRID_SIZE = GRID_WORLD_RESOLUTION;
|
||||||
|
var GRID_HALFSIZE = GRID_SIZE *0.5;
|
||||||
|
|
||||||
|
var ROOT_Z_OFFSET = -3;
|
||||||
|
var ROOT_Y_OFFSET = -0.1;
|
||||||
|
|
||||||
|
var TILE_UNIT = GRID_WORLD_SIZE / GRID_SIZE;
|
||||||
|
var TILE_DIM = { x: TILE_UNIT, y: TILE_UNIT, z: TILE_UNIT};
|
||||||
|
var GRID_TILE_OFFSET = Vec3.multiply(0.5, TILE_DIM);
|
||||||
|
|
||||||
|
var GRID_DROP_C = GRID_WORLD_DROP_HEIGHT / TILE_UNIT;
|
||||||
|
|
||||||
|
function updateWorldSizeAndResolution(size, res) {
|
||||||
|
GRID_WORLD_SIZE = size
|
||||||
|
GRID_WORLD_RESOLUTION = res;
|
||||||
|
|
||||||
|
GRID_SIZE = GRID_WORLD_RESOLUTION;
|
||||||
|
GRID_HALFSIZE = GRID_SIZE *0.5;
|
||||||
|
|
||||||
|
TILE_UNIT = GRID_WORLD_SIZE / GRID_SIZE;
|
||||||
|
TILE_DIM = { x: TILE_UNIT, y: TILE_UNIT, z: TILE_UNIT};
|
||||||
|
GRID_TILE_OFFSET = Vec3.multiply(0.5, TILE_DIM);
|
||||||
|
|
||||||
|
GRID_DROP_C = GRID_WORLD_DROP_HEIGHT / TILE_UNIT;
|
||||||
|
print("TILE_UNIT = " + TILE_UNIT)
|
||||||
|
print("GRID_DROP_C = " + GRID_DROP_C)
|
||||||
|
}
|
||||||
|
|
||||||
|
var OBJECT_DIM = { x: 0.5, y: 0.5, z: 0.5};
|
||||||
|
var CONE_DIM = { x: 0.3104, y: 0.3336, z: 0.3104};
|
||||||
|
var OBJECT_SPIN = { x: 0.5, y: 0.5, z: 0.5};
|
||||||
|
|
||||||
|
var shapeTypes = [
|
||||||
|
"none",
|
||||||
|
"box",
|
||||||
|
"sphere",
|
||||||
|
"compound",
|
||||||
|
"simple-hull",
|
||||||
|
"simple-compound",
|
||||||
|
"static-mesh"
|
||||||
|
];
|
||||||
|
|
||||||
|
function getTileColor(a, b, c) {
|
||||||
|
var offset = (Math.abs(a) + ((Math.abs(b) + (Math.abs(c) % 2)) % 2)) % 2;
|
||||||
|
var intensity = (1 - offset) * 128 + offset * 255;
|
||||||
|
return { red: intensity * (a % 4), green: intensity, blue: intensity * (b % 4) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function addObject(a, b, c, lifetime) {
|
||||||
|
var center = getStagePosOriAt(a, b, c).pos;
|
||||||
|
var offset = (Math.abs(a) + (Math.abs(b) % 2)) % 2;
|
||||||
|
var makePrim = (offset == 0 ? true : false) ;
|
||||||
|
if (makePrim == true) {
|
||||||
|
return (Entities.addEntity({
|
||||||
|
type: "Shape",
|
||||||
|
shape: "Sphere",
|
||||||
|
name: "object-" + a + b + c,
|
||||||
|
color: getTileColor(a, b, c),
|
||||||
|
position: center,
|
||||||
|
rotation: stageOrientation,
|
||||||
|
dimensions: OBJECT_DIM,
|
||||||
|
lifetime: (lifetime === undefined) ? DEFAULT_LIFETIME : lifetime,
|
||||||
|
shapeType:shapeTypes[2],
|
||||||
|
dynamic: true,
|
||||||
|
gravity:{"x":0,"y":-9.8,"z":0},
|
||||||
|
velocity:{"x":0,"y":0.02,"z":0},
|
||||||
|
angularVelocity:OBJECT_SPIN,
|
||||||
|
restitution:0.70,
|
||||||
|
friction:0.01,
|
||||||
|
damping:0.001,
|
||||||
|
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
return (Entities.addEntity({
|
||||||
|
type: "Model",
|
||||||
|
name: "object-" + a + b + c,
|
||||||
|
position: center,
|
||||||
|
rotation: stageOrientation,
|
||||||
|
dimensions: OBJECT_DIM,
|
||||||
|
lifetime: (lifetime === undefined) ? DEFAULT_LIFETIME : lifetime,
|
||||||
|
modelURL: "https://hifi-content.s3.amazonaws.com/jimi/props/cones/trafficCone.fbx",
|
||||||
|
shapeType:shapeTypes[4],
|
||||||
|
dynamic: true,
|
||||||
|
gravity:{"x":0,"y":-9.8,"z":0},
|
||||||
|
velocity:{"x":0,"y":0.02,"z":0},
|
||||||
|
angularVelocity:OBJECT_SPIN,
|
||||||
|
restitution:0.70,
|
||||||
|
friction:0.01,
|
||||||
|
damping:0.01,
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addObjectGrid(backdrop, lifetime) {
|
||||||
|
for (i = GRID_HALFSIZE; i > -GRID_HALFSIZE; i--) {
|
||||||
|
for (j = -GRID_HALFSIZE; j < GRID_HALFSIZE; j++) {
|
||||||
|
backdrop.push(addObject(i, j, GRID_DROP_C, lifetime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function addFloor(lifetime) {
|
||||||
|
var floorDim = { x:GRID_WORLD_SIZE + 2 * GRID_WORLD_MARGIN, y: TILE_DIM.y, z:GRID_WORLD_SIZE + 2 * GRID_WORLD_MARGIN};
|
||||||
|
var center = getStagePosOriAt(0, 0, -0.5).pos;
|
||||||
|
|
||||||
|
return (Entities.addEntity({
|
||||||
|
type: "Shape",
|
||||||
|
shape: "Cube",
|
||||||
|
name: "Floor",
|
||||||
|
color: { red: 20, green: 20, blue: 40 },
|
||||||
|
position: center,
|
||||||
|
rotation: stageOrientation,
|
||||||
|
dimensions: floorDim,
|
||||||
|
lifetime: (lifetime === undefined) ? DEFAULT_LIFETIME : lifetime,
|
||||||
|
|
||||||
|
shapeType:shapeTypes[1],
|
||||||
|
// dynamic: true,
|
||||||
|
// gravity:{"x":0,"y":-9.8,"z":0},
|
||||||
|
// velocity:{"x":0,"y":0.01,"z":0},
|
||||||
|
restitution:0.999,
|
||||||
|
friction:0.001,
|
||||||
|
damping:0.3,
|
||||||
|
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addZone(hasKeyLight, hasAmbient, lifetime) {
|
||||||
|
var zoneDim = { x:GRID_WORLD_SIZE + 2 * GRID_WORLD_MARGIN, y:GRID_WORLD_SIZE, z:GRID_WORLD_SIZE + 2 * GRID_WORLD_MARGIN};
|
||||||
|
var center = getStagePosOriAt(0, 0, -1).pos;
|
||||||
|
|
||||||
|
var lightDir = Vec3.normalize(Vec3.sum(Vec3.multiply(-1, Quat.getUp(stageOrientation)), Vec3.multiply(-1, Quat.getRight(stageOrientation))))
|
||||||
|
|
||||||
|
return (Entities.addEntity({
|
||||||
|
type: "Zone",
|
||||||
|
name: "Backdrop zone",
|
||||||
|
|
||||||
|
position: center,
|
||||||
|
rotation: stageOrientation,
|
||||||
|
dimensions: zoneDim,
|
||||||
|
lifetime: (lifetime === undefined) ? DEFAULT_LIFETIME : lifetime,
|
||||||
|
|
||||||
|
keyLightMode: "enabled",
|
||||||
|
skyboxMode: "enabled",
|
||||||
|
ambientLightMode: "enabled",
|
||||||
|
|
||||||
|
keyLight:{
|
||||||
|
intensity: 0.8 * hasKeyLight,
|
||||||
|
direction: {
|
||||||
|
"x": 0.037007175385951996,
|
||||||
|
"y": -0.7071067690849304,
|
||||||
|
"z": -0.7061376571655273
|
||||||
|
},
|
||||||
|
castShadows: true,
|
||||||
|
},
|
||||||
|
ambientLight: {
|
||||||
|
ambientIntensity: 1.0 * hasAmbient,
|
||||||
|
ambientURL: "https://github.com/highfidelity/hifi_tests/blob/master/assets/skymaps/Sky_Day-Sun-Mid-photo.ktx?raw=true",
|
||||||
|
},
|
||||||
|
|
||||||
|
hazeMode:"disabled",
|
||||||
|
backgroundMode:"skybox",
|
||||||
|
skybox:{
|
||||||
|
color: {"red":2,"green":2,"blue":2}, // Dark grey background
|
||||||
|
skyboxURL: "https://github.com/highfidelity/hifi_tests/blob/master/assets/skymaps/Sky_Day-Sun-Mid-photo.ktx?raw=true",
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTestScene(name, lifetime) {
|
||||||
|
var scene = [];
|
||||||
|
// scene.push(addFloor(lifetime));
|
||||||
|
// scene.push(addZone(true, true, lifetime));
|
||||||
|
|
||||||
|
addObjectGrid(scene, lifetime);
|
||||||
|
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Stage position and orientation initialised at setup
|
||||||
|
stageOrientation = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
|
||||||
|
stageRoot = {"x":0.0,"y":0.0,"z":0.0};
|
||||||
|
stageAxisA = Vec3.multiply(TILE_UNIT, Quat.getForward(stageOrientation));
|
||||||
|
stageAxisB = Vec3.multiply(TILE_UNIT, Quat.getRight(stageOrientation));
|
||||||
|
stageAxisC = Vec3.multiply(TILE_UNIT, Quat.getUp(stageOrientation));
|
||||||
|
|
||||||
|
setupScene = function (lifetime) {
|
||||||
|
MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0.0, 0.0, 0.0);
|
||||||
|
var orientation = MyAvatar.orientation;
|
||||||
|
orientation = Quat.safeEulerAngles(orientation);
|
||||||
|
orientation.x = 0;
|
||||||
|
orientation = Quat.fromVec3Degrees(orientation);
|
||||||
|
|
||||||
|
stageOrientation = orientation;
|
||||||
|
stageAxisA = Vec3.multiply(TILE_UNIT, Quat.getForward(stageOrientation));
|
||||||
|
stageAxisB = Vec3.multiply(TILE_UNIT, Quat.getRight(stageOrientation));
|
||||||
|
stageAxisC = Vec3.multiply(TILE_UNIT, Quat.getUp(stageOrientation));
|
||||||
|
|
||||||
|
stageRoot = Vec3.sum(MyAvatar.position, Vec3.multiply(-ROOT_Z_OFFSET, Quat.getForward(orientation)));
|
||||||
|
stageRoot = Vec3.sum(stageRoot, Vec3.multiply(ROOT_Y_OFFSET, Quat.getUp(orientation)));
|
||||||
|
|
||||||
|
return addTestScene("Physics_stage_backdrop", lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStagePosOriAt = function (a, b, c) {
|
||||||
|
var center = Vec3.sum(stageRoot, Vec3.multiply(a, stageAxisA));
|
||||||
|
center = Vec3.sum(center, Vec3.multiply(b, stageAxisB));
|
||||||
|
center = Vec3.sum(center, Vec3.multiply(c, stageAxisC));
|
||||||
|
|
||||||
|
return { "pos": center, "ori": stageOrientation};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var scene = []
|
||||||
|
|
||||||
|
createScene = function() {
|
||||||
|
clearScene();
|
||||||
|
scene = setupScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearScene = function() {
|
||||||
|
for (var i = 0; i < scene.length; i++) {
|
||||||
|
Entities.deleteEntity(scene[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeResolution = function(res) {
|
||||||
|
updateWorldSizeAndResolution(GRID_WORLD_SIZE, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
getResolution = function() {
|
||||||
|
return GRID_WORLD_RESOLUTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeSize = function(size) {
|
||||||
|
updateWorldSizeAndResolution(size, GRID_WORLD_RESOLUTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize = function() {
|
||||||
|
return GRID_WORLD_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getNumObjects = function() {
|
||||||
|
return GRID_SIZE * GRID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bumpUpFloor = function() {
|
||||||
|
print("bumpUpFloor")
|
||||||
|
if (scene.length > 0) {
|
||||||
|
Entities.editEntity(scene[0],{ velocity: {x: 0, y:-2.0,z: 0}})
|
||||||
|
}
|
||||||
|
}
|
120
scripts/developer/utilities/workload/workload.js
Normal file
120
scripts/developer/utilities/workload/workload.js
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
//
|
||||||
|
// Workload.js
|
||||||
|
// tablet-workload-engine app
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var TABLET_BUTTON_NAME = "Workload";
|
||||||
|
var QMLAPP_URL = Script.resolvePath("./workloadInspector.qml");
|
||||||
|
var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg");
|
||||||
|
var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg");
|
||||||
|
|
||||||
|
|
||||||
|
var onAppScreen = false;
|
||||||
|
|
||||||
|
function onClicked() {
|
||||||
|
if (onAppScreen) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
} else {
|
||||||
|
tablet.loadQMLSource(QMLAPP_URL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
var button = tablet.addButton({
|
||||||
|
text: TABLET_BUTTON_NAME,
|
||||||
|
icon: ICON_URL,
|
||||||
|
activeIcon: ACTIVE_ICON_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
var hasEventBridge = false;
|
||||||
|
|
||||||
|
function wireEventBridge(on) {
|
||||||
|
if (!tablet) {
|
||||||
|
print("Warning in wireEventBridge(): 'tablet' undefined!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (on) {
|
||||||
|
if (!hasEventBridge) {
|
||||||
|
tablet.fromQml.connect(fromQml);
|
||||||
|
hasEventBridge = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (hasEventBridge) {
|
||||||
|
tablet.fromQml.disconnect(fromQml);
|
||||||
|
hasEventBridge = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onScreenChanged(type, url) {
|
||||||
|
if (url === QMLAPP_URL) {
|
||||||
|
onAppScreen = true;
|
||||||
|
} else {
|
||||||
|
onAppScreen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.editProperties({isActive: onAppScreen});
|
||||||
|
wireEventBridge(onAppScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromQml(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
button.clicked.connect(onClicked);
|
||||||
|
tablet.screenChanged.connect(onScreenChanged);
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(function () {
|
||||||
|
if (onAppScreen) {
|
||||||
|
tablet.gotoHomeScreen();
|
||||||
|
}
|
||||||
|
button.clicked.disconnect(onClicked);
|
||||||
|
tablet.screenChanged.disconnect(onScreenChanged);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Script.include("./test_physics_scene.js")
|
||||||
|
|
||||||
|
function fromQml(message) {
|
||||||
|
switch (message.method) {
|
||||||
|
case "createScene":
|
||||||
|
createScene();
|
||||||
|
updateGridInQML()
|
||||||
|
break;
|
||||||
|
case "clearScene":
|
||||||
|
clearScene();
|
||||||
|
updateGridInQML()
|
||||||
|
break;
|
||||||
|
case "changeSize":
|
||||||
|
changeSize(message.params.count);
|
||||||
|
updateGridInQML()
|
||||||
|
break;
|
||||||
|
case "changeResolution":
|
||||||
|
changeResolution(message.params.count);
|
||||||
|
updateGridInQML()
|
||||||
|
break;
|
||||||
|
case "bumpUpFloor":
|
||||||
|
bumpUpFloor();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function updateGridInQML() {
|
||||||
|
sendToQml({method: "gridSize", params: { v: getSize() }})
|
||||||
|
sendToQml({method: "objectCount", params: { v: getNumObjects() }})
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendToQml(message) {
|
||||||
|
tablet.sendToQml(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGridInQML()
|
||||||
|
}());
|
372
scripts/developer/utilities/workload/workloadInspector.qml
Normal file
372
scripts/developer/utilities/workload/workloadInspector.qml
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
//
|
||||||
|
// _workload.qml
|
||||||
|
//
|
||||||
|
// Created by Sam Gateau on 3/1/2018
|
||||||
|
// Copyright 2018 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
import QtQuick 2.7
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
|
import "qrc:///qml/styles-uit"
|
||||||
|
import "qrc:///qml/controls-uit" as HifiControls
|
||||||
|
import "../render/configSlider"
|
||||||
|
import "../lib/jet/qml" as Jet
|
||||||
|
import "../lib/plotperf"
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
HifiConstants { id: hifi;}
|
||||||
|
id: _workload;
|
||||||
|
|
||||||
|
width: parent ? parent.width : 400
|
||||||
|
height: parent ? parent.height : 600
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
color: hifi.colors.baseGray;
|
||||||
|
|
||||||
|
function broadcastCreateScene() {
|
||||||
|
sendToScript({method: "createScene", params: { count:2 }});
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastClearScene() {
|
||||||
|
sendToScript({method: "clearScene", params: { count:2 }});
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastChangeSize(value) {
|
||||||
|
sendToScript({method: "changeSize", params: { count:value }});
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastChangeResolution(value) {
|
||||||
|
sendToScript({method: "changeResolution", params: { count:value }});
|
||||||
|
}
|
||||||
|
|
||||||
|
function broadcastBumpUpFloor(value) {
|
||||||
|
sendToScript({method: "bumpUpFloor", params: { count:0 }});
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromScript(message) {
|
||||||
|
switch (message.method) {
|
||||||
|
case "gridSize":
|
||||||
|
print("assigned value! " + message.params.v)
|
||||||
|
gridSizeLabel.text = ("Grid size [m] = " + message.params.v)
|
||||||
|
gridSize.setValue(message.params.v)
|
||||||
|
break;
|
||||||
|
case "resolution":
|
||||||
|
print("assigned value! " + message.params.v)
|
||||||
|
resolution.setValue(message.params.v)
|
||||||
|
break;
|
||||||
|
case "objectCount":
|
||||||
|
print("assigned objectCount! " + message.params.v)
|
||||||
|
objectCount.text = ("Num objects = " + message.params.v)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: stats
|
||||||
|
spacing: 5
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.margins: hifi.dimensions.contentMargin.x
|
||||||
|
//padding: hifi.dimensions.contentMargin.x
|
||||||
|
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Workload"
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "Freeze Views"
|
||||||
|
checked: Workload.getConfig("setupViews")["freezeViews"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("SpaceToRender")["freezeViews"] = checked, Workload.getConfig("setupViews")["freezeViews"] = checked; }
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "Use Avatar View"
|
||||||
|
checked: Workload.getConfig("setupViews")["useAvatarView"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("setupViews")["useAvatarView"] = checked; }
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "force View Horizontal"
|
||||||
|
checked: Workload.getConfig("setupViews")["forceViewHorizontal"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("setupViews")["forceViewHorizontal"] = checked; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "Simulate Secondary"
|
||||||
|
checked: Workload.getConfig("setupViews")["simulateSecondaryCamera"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("setupViews")["simulateSecondaryCamera"] = checked; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "Regulate View Ranges"
|
||||||
|
checked: Workload.getConfig("controlViews")["regulateViewRanges"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("controlViews")["regulateViewRanges"] = checked; }
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
visible: !Workload.getConfig("controlViews")["regulateViewRanges"]
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Column {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.horizontalCenter
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Back [m]"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
"R1:r1Back:250.0:0.0",
|
||||||
|
"R2:r2Back:250.0:0.0",
|
||||||
|
"R3:r3Back:250.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
config: Workload.getConfig("setupViews")
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
integral: true
|
||||||
|
|
||||||
|
labelAreaWidthScale: 0.4
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
anchors.left: parent.horizontalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Front [m]"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
"r1Front:500:1.0",
|
||||||
|
"r2Front:500:1.0",
|
||||||
|
"r3Front:500:1.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
showLabel: false
|
||||||
|
config: Workload.getConfig("setupViews")
|
||||||
|
property: modelData.split(":")[0]
|
||||||
|
max: modelData.split(":")[1]
|
||||||
|
min: modelData.split(":")[2]
|
||||||
|
integral: true
|
||||||
|
|
||||||
|
labelAreaWidthScale: 0.3
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*RowLayout {
|
||||||
|
visible: Workload.getConfig("controlViews")["regulateViewRanges"]
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
Column {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.horizontalCenter
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Back [m]"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
"R1:r1RangeBack:50.0:0.0",
|
||||||
|
"R2:r2RangeBack:50.0:0.0",
|
||||||
|
"R3:r3RangeBack:50.0:0.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
label: qsTr(modelData.split(":")[0])
|
||||||
|
config: Workload.getConfig("controlViews")
|
||||||
|
property: modelData.split(":")[1]
|
||||||
|
max: modelData.split(":")[2]
|
||||||
|
min: modelData.split(":")[3]
|
||||||
|
integral: true
|
||||||
|
|
||||||
|
labelAreaWidthScale: 0.4
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
anchors.left: parent.horizontalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Front [m]"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
Repeater {
|
||||||
|
model: [
|
||||||
|
"r1RangeFront:300:1.0",
|
||||||
|
"r2RangeFront:300:1.0",
|
||||||
|
"r3RangeFront:300:1.0"
|
||||||
|
]
|
||||||
|
ConfigSlider {
|
||||||
|
showLabel: false
|
||||||
|
config: Workload.getConfig("controlViews")
|
||||||
|
property: modelData.split(":")[0]
|
||||||
|
max: modelData.split(":")[1]
|
||||||
|
min: modelData.split(":")[2]
|
||||||
|
integral: true
|
||||||
|
|
||||||
|
labelAreaWidthScale: 0.3
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
property var controlViews: Workload.getConfig("controlViews")
|
||||||
|
|
||||||
|
PlotPerf {
|
||||||
|
title: "Timings"
|
||||||
|
height: 100
|
||||||
|
object: stats.controlViews
|
||||||
|
valueScale: 1.0
|
||||||
|
valueUnit: "ms"
|
||||||
|
plots: [
|
||||||
|
{
|
||||||
|
prop: "r2Timing",
|
||||||
|
label: "Physics + Collisions",
|
||||||
|
color: "#1AC567"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: "r3Timing",
|
||||||
|
label: "Kinematic + Update",
|
||||||
|
color: "#1A67C5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Separator {}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Numbers:";
|
||||||
|
}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "R1= " + Workload.getConfig("regionState")["numR1"];
|
||||||
|
}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "R2= " + Workload.getConfig("regionState")["numR2"];
|
||||||
|
}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "R3= " + Workload.getConfig("regionState")["numR3"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Display"
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "Show Proxies"
|
||||||
|
checked: Workload.getConfig("SpaceToRender")["showProxies"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("SpaceToRender")["showProxies"] = checked }
|
||||||
|
}
|
||||||
|
HifiControls.CheckBox {
|
||||||
|
boxSize: 20
|
||||||
|
text: "Show Views"
|
||||||
|
checked: Workload.getConfig("SpaceToRender")["showViews"]
|
||||||
|
onCheckedChanged: { Workload.getConfig("SpaceToRender")["showViews"] = checked }
|
||||||
|
}
|
||||||
|
Separator {}
|
||||||
|
HifiControls.Label {
|
||||||
|
text: "Test"
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
spacing: 5
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
HifiControls.Button {
|
||||||
|
text: "create scene"
|
||||||
|
onClicked: {
|
||||||
|
print("pressed")
|
||||||
|
_workload.broadcastCreateScene()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HifiControls.Button {
|
||||||
|
text: "clear scene"
|
||||||
|
onClicked: {
|
||||||
|
print("pressed")
|
||||||
|
_workload.broadcastClearScene()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*HifiControls.Button {
|
||||||
|
text: "bump floor"
|
||||||
|
onClicked: {
|
||||||
|
print("pressed")
|
||||||
|
_workload.broadcastBumpUpFloor()
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
HifiControls.Label {
|
||||||
|
id: gridSizeLabel
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: "Grid side size [m]"
|
||||||
|
}
|
||||||
|
HifiControls.Slider {
|
||||||
|
id: gridSize
|
||||||
|
stepSize: 1.0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.topMargin: 0
|
||||||
|
minimumValue: 1
|
||||||
|
maximumValue: 200
|
||||||
|
value: 100
|
||||||
|
|
||||||
|
onValueChanged: { _workload.broadcastChangeSize(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
HifiControls.Label {
|
||||||
|
id: objectCount
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
text: "Num objects"
|
||||||
|
}
|
||||||
|
HifiControls.Slider {
|
||||||
|
id: resolution
|
||||||
|
stepSize: 1.0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 0
|
||||||
|
anchors.topMargin: 0
|
||||||
|
minimumValue: 5
|
||||||
|
maximumValue: 75
|
||||||
|
value: 5
|
||||||
|
|
||||||
|
onValueChanged: { _workload.broadcastChangeResolution(value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
Separator {}
|
||||||
|
|
||||||
|
/*Jet.TaskList {
|
||||||
|
rootConfig: Workload
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
height: 300
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES FOLDER "Tests/manual-tests/")
|
||||||
|
|
||||||
# link in the shared libraries
|
# link in the shared libraries
|
||||||
link_hifi_libraries(
|
link_hifi_libraries(
|
||||||
shared task networking animation
|
shared task workload networking animation
|
||||||
ktx image octree gl gpu ${PLATFORM_GL_BACKEND}
|
ktx image octree gl gpu ${PLATFORM_GL_BACKEND}
|
||||||
render render-utils
|
render render-utils
|
||||||
graphics fbx model-networking graphics-scripting
|
graphics fbx model-networking graphics-scripting
|
||||||
|
|
|
@ -55,7 +55,9 @@ void SpaceTests::testOverlaps() {
|
||||||
float newRadius = 1.0f;
|
float newRadius = 1.0f;
|
||||||
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, far + newRadius - DELTA);
|
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, far + newRadius - DELTA);
|
||||||
workload::Space::Sphere newSphere(newPosition, newRadius);
|
workload::Space::Sphere newSphere(newPosition, newRadius);
|
||||||
space.updateProxy(proxyId, newSphere);
|
std::vector<workload::Space::ProxyUpdate> updates;
|
||||||
|
updates.push_back(workload::Space::ProxyUpdate(proxyId, newSphere));
|
||||||
|
space.updateProxies(updates);
|
||||||
Changes changes;
|
Changes changes;
|
||||||
space.categorizeAndGetChanges(changes);
|
space.categorizeAndGetChanges(changes);
|
||||||
QVERIFY(changes.size() == 1);
|
QVERIFY(changes.size() == 1);
|
||||||
|
@ -68,7 +70,9 @@ void SpaceTests::testOverlaps() {
|
||||||
float newRadius = 1.0f;
|
float newRadius = 1.0f;
|
||||||
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, mid + newRadius - DELTA);
|
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, mid + newRadius - DELTA);
|
||||||
workload::Space::Sphere newSphere(newPosition, newRadius);
|
workload::Space::Sphere newSphere(newPosition, newRadius);
|
||||||
space.updateProxy(proxyId, newSphere);
|
std::vector<workload::Space::ProxyUpdate> updates;
|
||||||
|
updates.push_back(workload::Space::ProxyUpdate(proxyId, newSphere));
|
||||||
|
space.updateProxies(updates);
|
||||||
Changes changes;
|
Changes changes;
|
||||||
space.categorizeAndGetChanges(changes);
|
space.categorizeAndGetChanges(changes);
|
||||||
QVERIFY(changes.size() == 1);
|
QVERIFY(changes.size() == 1);
|
||||||
|
@ -81,7 +85,9 @@ void SpaceTests::testOverlaps() {
|
||||||
float newRadius = 1.0f;
|
float newRadius = 1.0f;
|
||||||
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, near + newRadius - DELTA);
|
glm::vec3 newPosition = viewCenter + glm::vec3(0.0f, 0.0f, near + newRadius - DELTA);
|
||||||
workload::Space::Sphere newSphere(newPosition, newRadius);
|
workload::Space::Sphere newSphere(newPosition, newRadius);
|
||||||
space.updateProxy(proxyId, newSphere);
|
std::vector<workload::Space::ProxyUpdate> updates;
|
||||||
|
updates.push_back(workload::Space::ProxyUpdate(proxyId, newSphere));
|
||||||
|
space.updateProxies(updates);
|
||||||
Changes changes;
|
Changes changes;
|
||||||
space.categorizeAndGetChanges(changes);
|
space.categorizeAndGetChanges(changes);
|
||||||
QVERIFY(changes.size() == 1);
|
QVERIFY(changes.size() == 1);
|
||||||
|
@ -92,7 +98,9 @@ void SpaceTests::testOverlaps() {
|
||||||
|
|
||||||
{ // delete proxy
|
{ // delete proxy
|
||||||
// NOTE: atm deleting a proxy doesn't result in a "Change"
|
// NOTE: atm deleting a proxy doesn't result in a "Change"
|
||||||
space.deleteProxy(proxyId);
|
std::vector<int32_t> deadProxies;
|
||||||
|
deadProxies.push_back(proxyId);
|
||||||
|
space.deleteProxies(deadProxies);
|
||||||
Changes changes;
|
Changes changes;
|
||||||
space.categorizeAndGetChanges(changes);
|
space.categorizeAndGetChanges(changes);
|
||||||
QVERIFY(changes.size() == 0);
|
QVERIFY(changes.size() == 0);
|
||||||
|
@ -145,9 +153,9 @@ void SpaceTests::benchmark() {
|
||||||
uint32_t numProxies[] = { 100, 1000, 10000, 100000 };
|
uint32_t numProxies[] = { 100, 1000, 10000, 100000 };
|
||||||
uint32_t numTests = 4;
|
uint32_t numTests = 4;
|
||||||
std::vector<uint64_t> timeToAddAll;
|
std::vector<uint64_t> timeToAddAll;
|
||||||
std::vector<uint64_t> timeToRemoveAll;
|
|
||||||
std::vector<uint64_t> timeToMoveView;
|
std::vector<uint64_t> timeToMoveView;
|
||||||
std::vector<uint64_t> timeToMoveProxies;
|
std::vector<uint64_t> timeToMoveProxies;
|
||||||
|
std::vector<uint64_t> timeToRemoveAll;
|
||||||
for (uint32_t i = 0; i < numTests; ++i) {
|
for (uint32_t i = 0; i < numTests; ++i) {
|
||||||
|
|
||||||
workload::Space space;
|
workload::Space space;
|
||||||
|
|
Loading…
Reference in a new issue