mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +02:00
templatize the ViewFrustum-relative sort algorithm
This commit is contained in:
parent
f47185b2f6
commit
8707c76a6a
2 changed files with 149 additions and 38 deletions
|
@ -38,6 +38,22 @@
|
|||
|
||||
#include "RenderableWebEntityItem.h"
|
||||
|
||||
// implement these methods BEFORE including PrioritySortUtil.h
|
||||
namespace PrioritySortUtil {
|
||||
glm::vec3 getObjectPosition(const EntityRendererPointer& object) {
|
||||
return object->getEntity()->getPosition();
|
||||
}
|
||||
|
||||
float getObjectRadius(const EntityRendererPointer& object) {
|
||||
return 0.5f * object->getEntity()->getQueryAACube().getScale();
|
||||
}
|
||||
|
||||
uint64_t getObjectAge(const EntityRendererPointer& object) {
|
||||
return object->getUpdateTime();
|
||||
}
|
||||
}
|
||||
#include <PrioritySortUtil.h>
|
||||
|
||||
size_t std::hash<EntityItemID>::operator()(const EntityItemID& id) const { return qHash(id); }
|
||||
std::function<bool()> EntityTreeRenderer::_entitiesShouldFadeFunction;
|
||||
|
||||
|
@ -338,47 +354,16 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
// we expect the cost to updating all renderables to exceed available time budget
|
||||
// so we first sort by priority and update in order until out of time
|
||||
uint64_t sortStart = usecTimestampNow();
|
||||
std::priority_queue<SortableEntityRenderer> sortedRenderables;
|
||||
using SortableRenderer = PrioritySortUtil::Sortable<EntityRendererPointer>;
|
||||
std::priority_queue< SortableRenderer > sortedRenderables;
|
||||
{
|
||||
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||
PrioritySortUtil::PriorityCalculator<EntityRendererPointer> priorityCalculator(view);
|
||||
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr = _renderablesToUpdate.begin();
|
||||
glm::vec3 viewCenter = view.getPosition();
|
||||
glm::vec3 forward = view.getDirection();
|
||||
const float OUT_OF_VIEW_PENALTY = -10.0f;
|
||||
while (itr != _renderablesToUpdate.end()) {
|
||||
// priority = weighted linear combination of:
|
||||
// (a) apparentSize
|
||||
// (b) proximity to center of view
|
||||
// (c) time since last update
|
||||
EntityItemPointer entity = itr->second->getEntity();
|
||||
glm::vec3 entityPosition = entity->getPosition();
|
||||
glm::vec3 offset = entityPosition - viewCenter;
|
||||
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
||||
|
||||
float diameter = entity->getQueryAACube().getScale();
|
||||
float apparentSize = diameter / distance;
|
||||
float cosineAngle = glm::dot(offset, forward) / distance;
|
||||
float age = (float)(sortStart - itr->second->getUpdateTime()) / (float)(USECS_PER_SECOND);
|
||||
|
||||
// NOTE: we are adding values of different units to get a single measure of "priority".
|
||||
// Thus we multiply each component by a conversion "weight" that scales its units relative to the others.
|
||||
// These weights are pure magic tuning and should be hard coded in the relation below,
|
||||
// but are currently exposed for anyone who would like to explore fine tuning:
|
||||
const float APPARENT_SIZE_COEFFICIENT = 1.0f;
|
||||
const float CENTER_SORT_COEFFICIENT = 0.5f;
|
||||
const float AGE_SORT_COEFFICIENT = 0.25f;
|
||||
float priority = APPARENT_SIZE_COEFFICIENT * apparentSize
|
||||
+ CENTER_SORT_COEFFICIENT * cosineAngle
|
||||
+ AGE_SORT_COEFFICIENT * age;
|
||||
|
||||
// decrement priority of things outside keyhole
|
||||
if (distance > view.getCenterRadius()) {
|
||||
if (!view.sphereIntersectsFrustum(entityPosition, 0.5f * diameter)) {
|
||||
priority += OUT_OF_VIEW_PENALTY;
|
||||
}
|
||||
}
|
||||
|
||||
sortedRenderables.push(SortableEntityRenderer(itr->second, priority));
|
||||
float priority = priorityCalculator.computePriority(itr->second);
|
||||
SortableRenderer entry(itr->second, priority);
|
||||
sortedRenderables.push(entry);
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +383,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
|||
std::unordered_map<EntityItemID, EntityRendererPointer>::iterator itr;
|
||||
size_t numSorted = sortedRenderables.size();
|
||||
while (!sortedRenderables.empty() && usecTimestampNow() < expiry) {
|
||||
const EntityRendererPointer& renderer = sortedRenderables.top().renderer;
|
||||
const EntityRendererPointer& renderer = sortedRenderables.top().getObject();
|
||||
renderer->updateInScene(scene, transaction);
|
||||
_renderablesToUpdate.erase(renderer->getEntity()->getID());
|
||||
sortedRenderables.pop();
|
||||
|
|
126
libraries/shared/src/PrioritySortUtil.h
Normal file
126
libraries/shared/src/PrioritySortUtil.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
//
|
||||
// PrioritySortUtil.h
|
||||
//
|
||||
// Created by Andrew Meadows on 2017-11-08
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_PrioritySortUtil_h
|
||||
#define hifi_PrioritySortUtil_h
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "ViewFrustum.h"
|
||||
|
||||
namespace PrioritySortUtil {
|
||||
// PrioritySortUtil is a helper template for sorting 3D objects relative to a ViewFrustum.
|
||||
// To use this utility:
|
||||
//
|
||||
// (1) Declare and implent the following methods for your "object" type T:
|
||||
//
|
||||
// glm::vec3 PrioritySortUtil<typename T>::getObjectPosition(const T&);
|
||||
// float PrioritySortUtil<typename T>::getObjectRadius(const T&);
|
||||
// uint64_t PrioritySortUtil<typename T>::getObjectAge(const T&);
|
||||
//
|
||||
// (2) Below the implementation in (1):
|
||||
/
|
||||
// #include <PrioritySortUtil.h>
|
||||
//
|
||||
// (3) Create a PriorityCalculator instance:
|
||||
//
|
||||
// PrioritySortUtil::PriorityCalculator<T> calculator(viewFrustum);
|
||||
//
|
||||
// (4) Loop over your objects and insert the into a priority_queue:
|
||||
//
|
||||
// std::priority_queue< PrioritySortUtil::Sortable<T> > sortedObjects;
|
||||
// for (T obj in objects) {
|
||||
// float priority = calculator.computePriority(obj);
|
||||
// PrioritySortUtil::Sortable<T> entry(obj, priority);
|
||||
// sortedObjects.push(entry);
|
||||
// }
|
||||
|
||||
template <typename T>
|
||||
class Sortable {
|
||||
public:
|
||||
Sortable(const T& object, float sortPriority) : _object(object), _priority(sortPriority) {}
|
||||
const T& getObject() const { return _object; }
|
||||
void setPriority(float priority) { _priority = priority; }
|
||||
bool operator<(const Sortable& other) const { return _priority < other._priority; }
|
||||
private:
|
||||
T _object;
|
||||
float _priority;
|
||||
};
|
||||
|
||||
constexpr float DEFAULT_ANGULAR_COEF { 1.0f };
|
||||
constexpr float DEFAULT_CENTER_COEF { 0.5f };
|
||||
constexpr float DEFAULT_AGE_COEF { 0.25f / (float)(USECS_PER_SECOND) };
|
||||
|
||||
template <typename T>
|
||||
class PriorityCalculator {
|
||||
public:
|
||||
PriorityCalculator() = delete;
|
||||
|
||||
PriorityCalculator(const ViewFrustum& view) : _view(view) {
|
||||
cacheView();
|
||||
}
|
||||
|
||||
PriorityCalculator(const ViewFrustum& view, float angularWeight, float centerWeight, float ageWeight)
|
||||
: _view(view), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
|
||||
{
|
||||
cacheView();
|
||||
}
|
||||
|
||||
void setView(const ViewFrustum& view) { _view = view; }
|
||||
|
||||
void setWeights(float angularWeight, float centerWeight, float ageWeight) {
|
||||
_angularWeight = angularWeight;
|
||||
_centerWeight = centerWeight;
|
||||
_ageWeight = ageWeight;
|
||||
}
|
||||
|
||||
float computePriority(T& object) const {
|
||||
// priority = weighted linear combination of multiple values:
|
||||
// (a) angular size
|
||||
// (b) proximity to center of view
|
||||
// (c) time since last update
|
||||
// where the relative "weights" are tuned to scale the contributing values into units of "priority".
|
||||
|
||||
glm::vec3 position = PrioritySortUtil::getObjectPosition(object);
|
||||
glm::vec3 offset = position - _viewPosition;
|
||||
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
|
||||
float radius = PrioritySortUtil::getObjectRadius(object);
|
||||
|
||||
float priority = _angularWeight * (radius / distance)
|
||||
+ _centerWeight * (glm::dot(offset, _viewForward) / distance)
|
||||
+ _ageWeight * (float)(usecTimestampNow() - PrioritySortUtil::getObjectAge(object));
|
||||
|
||||
// decrement priority of things outside keyhole
|
||||
if (distance + radius > _viewRadius) {
|
||||
if (!_view.sphereIntersectsFrustum(position, radius)) {
|
||||
constexpr float OUT_OF_VIEW_PENALTY = -10.0f;
|
||||
priority += OUT_OF_VIEW_PENALTY;
|
||||
}
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
|
||||
private:
|
||||
void cacheView() {
|
||||
// assuming we'll prioritize many objects: cache these values
|
||||
_viewPosition = _view.getPosition();
|
||||
_viewForward = _view.getDirection();
|
||||
_viewRadius = _view.getCenterRadius();
|
||||
}
|
||||
|
||||
ViewFrustum _view;
|
||||
glm::vec3 _viewPosition;
|
||||
glm::vec3 _viewForward;
|
||||
float _viewRadius;
|
||||
float _angularWeight { DEFAULT_ANGULAR_COEF };
|
||||
float _centerWeight { DEFAULT_CENTER_COEF };
|
||||
float _ageWeight { DEFAULT_AGE_COEF };
|
||||
};
|
||||
}
|
||||
#endif // hifi_PrioritySortUtil_h
|
Loading…
Reference in a new issue