add deeply nested skyboxes

This commit is contained in:
Zach Pomerantz 2016-08-29 13:57:14 -07:00
parent 1971063b9a
commit f30308b68c
2 changed files with 271 additions and 121 deletions

View file

@ -52,7 +52,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
_viewState(viewState), _viewState(viewState),
_scriptingServices(scriptingServices), _scriptingServices(scriptingServices),
_displayModelBounds(false), _displayModelBounds(false),
_dontDoPrecisionPicking(false) _dontDoPrecisionPicking(false),
_layeredZones(this)
{ {
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory) REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
@ -135,8 +136,11 @@ void EntityTreeRenderer::clear() {
_entitiesInScene.clear(); _entitiesInScene.clear();
// reset the zone to the default (while we load the next scene) // reset the zone to the default (while we load the next scene)
_bestZone = nullptr; _layeredZones.clear();
applyZonePropertiesToScene(_bestZone); _pendingAmbientTexture = _pendingSkyboxTexture = false;
_ambientTexture.clear();
_skyboxTexture.clear();
applyZoneAndHasSkybox(nullptr);
OctreeRenderer::clear(); OctreeRenderer::clear();
} }
@ -192,10 +196,10 @@ void EntityTreeRenderer::update() {
// If we haven't already updated and previously attempted to load a texture, // If we haven't already updated and previously attempted to load a texture,
// check if the texture loaded and apply it // check if the texture loaded and apply it
if (!updated && ( if (!updated &&
(_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())) || ((_pendingSkyboxTexture && _skyboxTexture && _skyboxTexture->isLoaded()) ||
(_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())))) { (_pendingAmbientTexture && _ambientTexture && _ambientTexture->isLoaded()))) {
applyZonePropertiesToScene(_bestZone); applySkyboxAndHasAmbient();
} }
// Even if we're not moving the mouse, if we started clicking on an entity and we have // Even if we're not moving the mouse, if we started clicking on an entity and we have
@ -210,7 +214,7 @@ void EntityTreeRenderer::update() {
deleteReleasedModels(); deleteReleasedModels();
} }
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, 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
QVector<EntityItemPointer> foundEntities; QVector<EntityItemPointer> foundEntities;
@ -220,12 +224,10 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
_tree->withReadLock([&] { _tree->withReadLock([&] {
// FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster // FIXME - if EntityTree had a findEntitiesContainingPoint() this could theoretically be a little faster
std::static_pointer_cast<EntityTree>(_tree)->findEntities(avatarPosition, radius, foundEntities); std::static_pointer_cast<EntityTree>(_tree)->findEntities(_avatarPosition, radius, foundEntities);
// Whenever you're in an intersection between zones, we will always choose the smallest zone. LayeredZones oldLayeredZones(std::move(_layeredZones));
auto oldBestZone = _bestZone; _layeredZones.clear();
_bestZone = nullptr; // NOTE: Is this what we want?
_bestZoneVolume = std::numeric_limits<float>::max();
// create a list of entities that actually contain the avatar's position // create a list of entities that actually contain the avatar's position
for (auto& entity : foundEntities) { for (auto& entity : foundEntities) {
@ -239,38 +241,34 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
if (isZone || hasScript) { if (isZone || hasScript) {
// now check to see if the point contains our entity, this can be expensive if // now check to see if the point contains our entity, this can be expensive if
// the entity has a collision hull // the entity has a collision hull
if (entity->contains(avatarPosition)) { if (entity->contains(_avatarPosition)) {
if (entitiesContainingAvatar) { if (entitiesContainingAvatar) {
*entitiesContainingAvatar << entity->getEntityItemID(); *entitiesContainingAvatar << entity->getEntityItemID();
} }
// if this entity is a zone and visible, determine if it is the bestZone // if this entity is a zone and visible, determine if it is the bestZone
if (isZone && entity->getVisible()) { if (isZone && entity->getVisible()) {
float entityVolumeEstimate = entity->getVolumeEstimate(); auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
if (entityVolumeEstimate < _bestZoneVolume) { _layeredZones.insert(zone);
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
} else if (entityVolumeEstimate == _bestZoneVolume) {
// in the case of the volume being equal, we will use the
// EntityItemID to deterministically pick one entity over the other
if (!_bestZone) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
} else if (entity->getEntityItemID() < _bestZone->getEntityItemID()) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
}
}
} }
} }
} }
} }
if (_bestZone != oldBestZone) { // check if our layered zones have changed
applyZonePropertiesToScene(_bestZone); if (_layeredZones.empty()) {
didUpdate = true; if (oldLayeredZones.empty()) {
return;
}
} else if (!oldLayeredZones.empty()) {
if (_layeredZones.contains(oldLayeredZones)) {
return;
}
} }
_layeredZones.apply();
didUpdate = true;
}); });
return didUpdate; return didUpdate;
} }
@ -286,13 +284,14 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
// if some amount of time has elapsed since we last checked. We check the time // if some amount of time has elapsed since we last checked. We check the time
// elapsed because zones or entities might have been created "around us" while we've // elapsed because zones or entities might have been created "around us" while we've
// been stationary // been stationary
auto movedEnough = glm::distance(avatarPosition, _lastAvatarPosition) > ZONE_CHECK_DISTANCE; auto movedEnough = glm::distance(avatarPosition, _avatarPosition) > ZONE_CHECK_DISTANCE;
auto enoughTimeElapsed = (now - _lastZoneCheck) > ZONE_CHECK_INTERVAL; auto enoughTimeElapsed = (now - _lastZoneCheck) > ZONE_CHECK_INTERVAL;
if (movedEnough || enoughTimeElapsed) { if (movedEnough || enoughTimeElapsed) {
_avatarPosition = avatarPosition;
_lastZoneCheck = now; _lastZoneCheck = now;
QVector<EntityItemID> entitiesContainingAvatar; QVector<EntityItemID> entitiesContainingAvatar;
didUpdate = findBestZoneAndMaybeContainingEntities(avatarPosition, &entitiesContainingAvatar); didUpdate = findBestZoneAndMaybeContainingEntities(&entitiesContainingAvatar);
// Note: at this point we don't need to worry about the tree being locked, because we only deal with // Note: at this point we don't need to worry about the tree being locked, because we only deal with
// EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts // EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts
@ -318,7 +317,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
} }
} }
_currentEntitiesInside = entitiesContainingAvatar; _currentEntitiesInside = entitiesContainingAvatar;
_lastAvatarPosition = avatarPosition;
} }
} }
return didUpdate; return didUpdate;
@ -342,24 +340,20 @@ void EntityTreeRenderer::leaveAllEntities() {
void EntityTreeRenderer::forceRecheckEntities() { void EntityTreeRenderer::forceRecheckEntities() {
// make sure our "last avatar position" is something other than our current position, // make sure our "last avatar position" is something other than our current position,
// so that on our next chance, we'll check for enter/leave entity events. // so that on our next chance, we'll check for enter/leave entity events.
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE); _avatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
} }
bool EntityTreeRenderer::applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone) {
auto textureCache = DependencyManager::get<TextureCache>(); auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>(); auto scene = DependencyManager::get<SceneScriptingInterface>();
auto sceneStage = scene->getStage(); auto sceneStage = scene->getStage();
auto skyStage = scene->getSkyStage(); auto skyStage = scene->getSkyStage();
auto sceneKeyLight = sceneStage->getKeyLight(); auto sceneKeyLight = sceneStage->getKeyLight();
// Skybox and procedural skybox data
auto skybox = std::dynamic_pointer_cast<ProceduralSkybox>(skyStage->getSkybox());
// If there is no zone, use the default background // If there is no zone, use the default background
if (!zone) { if (!zone) {
_zoneUserData = QString(); _zoneUserData = QString();
skybox->clear(); skyStage->getSkybox()->clear();
_pendingSkyboxTexture = false; _pendingSkyboxTexture = false;
_skyboxTexture.clear(); _skyboxTexture.clear();
@ -371,7 +365,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
sceneKeyLight->setAmbientMap(nullptr); sceneKeyLight->setAmbientMap(nullptr);
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
return; return false;
} }
// Set the keylight // Set the keylight
@ -394,35 +388,34 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
} }
// Set the ambient texture // Set the ambient texture
bool isAmbientTextureSet = false;
if (zone->getKeyLightProperties().getAmbientURL().isEmpty()) { if (zone->getKeyLightProperties().getAmbientURL().isEmpty()) {
_pendingAmbientTexture = false; _pendingAmbientTexture = false;
_ambientTexture.clear(); _ambientTexture.clear();
} else { } else {
_ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), NetworkTexture::CUBE_TEXTURE);
_pendingAmbientTexture = true; _pendingAmbientTexture = true;
_ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), NetworkTexture::CUBE_TEXTURE);
if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
auto texture = _ambientTexture->getGPUTexture();
if (texture) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientTextureSet = true;
} else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << zone->getKeyLightProperties().getAmbientURL();
}
}
} }
// Set the skybox texture // Set the skybox texture
return layerZoneAndHasSkybox(zone);
}
bool EntityTreeRenderer::layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
assert(zone);
auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>();
auto skyStage = scene->getSkyStage();
auto skybox = skyStage->getSkybox();
bool hasSkybox = false;
switch (zone->getBackgroundMode()) { switch (zone->getBackgroundMode()) {
case BACKGROUND_MODE_SKYBOX: { case BACKGROUND_MODE_SKYBOX:
skybox->setColor(zone->getSkyboxProperties().getColorVec3()); skybox->setColor(zone->getSkyboxProperties().getColorVec3());
if (_zoneUserData != zone->getUserData()) { if (_zoneUserData != zone->getUserData()) {
_zoneUserData = zone->getUserData(); _zoneUserData = zone->getUserData();
skybox->parse(_zoneUserData); std::dynamic_pointer_cast<ProceduralSkybox>(skybox)->parse(_zoneUserData);
} }
if (zone->getSkyboxProperties().getURL().isEmpty()) { if (zone->getSkyboxProperties().getURL().isEmpty()) {
skybox->setCubemap(nullptr); skybox->setCubemap(nullptr);
@ -432,29 +425,11 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
// Update the Texture of the Skybox with the one pointed by this zone // Update the Texture of the Skybox with the one pointed by this zone
_skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), NetworkTexture::CUBE_TEXTURE); _skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), NetworkTexture::CUBE_TEXTURE);
_pendingSkyboxTexture = true; _pendingSkyboxTexture = true;
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
_pendingSkyboxTexture = false;
auto texture = _skyboxTexture->getGPUTexture();
if (texture) {
skybox->setCubemap(texture);
if (!isAmbientTextureSet) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientTextureSet = true;
}
} else {
qCDebug(entitiesrenderer) << "Failed to load skybox texture:" << zone->getSkyboxProperties().getURL();
skybox->setCubemap(nullptr);
}
} else {
skybox->setCubemap(nullptr);
}
} }
applySkyboxAndHasAmbient();
skyStage->setBackgroundMode(model::SunSkyStage::SKY_BOX); skyStage->setBackgroundMode(model::SunSkyStage::SKY_BOX);
hasSkybox = true;
break; break;
}
case BACKGROUND_MODE_INHERIT: case BACKGROUND_MODE_INHERIT:
default: default:
@ -466,7 +441,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
_pendingSkyboxTexture = false; _pendingSkyboxTexture = false;
// Let the application background through // Let the application background through
if (isAmbientTextureSet) { if (applySkyboxAndHasAmbient()) {
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE); skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE);
} else { } else {
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE); skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE);
@ -474,10 +449,56 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
break; break;
} }
if (!isAmbientTextureSet) { return hasSkybox;
}
bool EntityTreeRenderer::applySkyboxAndHasAmbient() {
auto textureCache = DependencyManager::get<TextureCache>();
auto scene = DependencyManager::get<SceneScriptingInterface>();
auto sceneStage = scene->getStage();
auto skyStage = scene->getSkyStage();
auto sceneKeyLight = sceneStage->getKeyLight();
auto skybox = skyStage->getSkybox();
bool isAmbientSet = false;
if (_ambientTexture && _ambientTexture->isLoaded()) {
_pendingAmbientTexture = false;
auto texture = _ambientTexture->getGPUTexture();
if (texture) {
isAmbientSet = true;
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
} else {
qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL();
}
}
if (_skyboxTexture && _skyboxTexture->isLoaded()) {
_pendingSkyboxTexture = false;
auto texture = _skyboxTexture->getGPUTexture();
if (texture) {
skybox->setCubemap(texture);
if (!isAmbientSet) {
sceneKeyLight->setAmbientSphere(texture->getIrradiance());
sceneKeyLight->setAmbientMap(texture);
isAmbientSet = true;
}
} else {
qCDebug(entitiesrenderer) << "Failed to load skybox texture:" << _skyboxTexture->getURL();
skybox->setCubemap(nullptr);
}
} else {
skybox->setCubemap(nullptr);
}
if (!isAmbientSet) {
sceneKeyLight->resetAmbientSphere(); sceneKeyLight->resetAmbientSphere();
sceneKeyLight->setAmbientMap(nullptr); sceneKeyLight->setAmbientMap(nullptr);
} }
return isAmbientSet;
} }
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) { const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
@ -1046,21 +1067,119 @@ void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
} }
void EntityTreeRenderer::updateZone(const EntityItemID& id) { void EntityTreeRenderer::updateZone(const EntityItemID& id) {
if (!_bestZone) { // Get in the zone!
// Get in the zone! auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(getTree()->findEntityByEntityItemID(id));
auto zone = getTree()->findEntityByEntityItemID(id); if (zone && zone->contains(_avatarPosition)) {
if (zone && zone->contains(_lastAvatarPosition)) { _layeredZones.update(zone);
_currentEntitiesInside << id;
emit enterEntity(id);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(id, "enterEntity");
}
if (zone->getVisible()) {
_bestZone = std::dynamic_pointer_cast<ZoneEntityItem>(zone);
}
}
}
if (_bestZone && _bestZone->getID() == id) {
applyZonePropertiesToScene(_bestZone);
} }
} }
EntityTreeRenderer::LayeredZones::LayeredZones(LayeredZones&& other) {
// In a swap:
// > All iterators and references remain valid. The past-the-end iterator is invalidated.
bool isSkyboxLayerValid = (other._skyboxLayer != other.end());
swap(other);
_map.swap(other._map);
_skyboxLayer = other._skyboxLayer;
if (!isSkyboxLayerValid) {
_skyboxLayer = end();
}
}
void EntityTreeRenderer::LayeredZones::clear() {
std::set<LayeredZone>::clear();
_map.clear();
_skyboxLayer = end();
}
std::pair<EntityTreeRenderer::LayeredZones::iterator, bool> EntityTreeRenderer::LayeredZones::insert(const LayeredZone& layer) {
iterator it;
bool success;
std::tie(it, success) = std::set<LayeredZone>::insert(layer);
if (success) {
_map.emplace(it->id, it);
}
return { it, success };
}
void EntityTreeRenderer::LayeredZones::apply() {
assert(_entityTreeRenderer);
applyPartial(begin());
}
void EntityTreeRenderer::LayeredZones::update(std::shared_ptr<ZoneEntityItem> zone) {
assert(_entityTreeRenderer);
bool isVisible = zone->isVisible();
if (empty() && isVisible) {
// there are no zones: set this one
insert(zone);
apply();
return;
} else {
LayeredZone zoneLayer(zone);
// should we update? only if this zone is tighter than the current skybox zone
bool shouldUpdate = false;
if (_skyboxLayer == end() || zoneLayer <= *_skyboxLayer) {
shouldUpdate = true;
}
// find this zone's layer, if it exists
iterator layer = end();
auto it = _map.find(zoneLayer.id);
if (it != _map.end()) {
layer = it->second;
// if the volume changed, we need to resort the layer (reinsertion)
// if the visibility changed, we need to erase the layer
if (zoneLayer.volume != layer->volume || !isVisible) {
erase(layer);
_map.erase(it);
layer = end();
}
}
// (re)insert this zone's layer if necessary
if (layer == end() && isVisible) {
std::tie(layer, std::ignore) = insert(zoneLayer);
_map.emplace(layer->id, layer);
}
if (shouldUpdate) {
applyPartial(layer);
}
}
}
void EntityTreeRenderer::LayeredZones::applyPartial(iterator layer) {
bool hasSkybox = false;
_skyboxLayer = end();
// empty
if (layer == end()) {
assert(layer == begin());
_entityTreeRenderer->applyZoneAndHasSkybox(nullptr);
return;
}
if (layer == begin()) {
hasSkybox = _entityTreeRenderer->applyZoneAndHasSkybox(layer->zone);
}
if (layer != end()) {
while (!hasSkybox && ++layer != end()) {
hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
}
}
_skyboxLayer = layer;
}
bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
return std::equal(other.begin(), other._skyboxLayer, begin());
}

View file

@ -95,7 +95,7 @@ public:
// For Scene.shouldRenderEntities // For Scene.shouldRenderEntities
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; } QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _bestZone; } std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _layeredZones.getZone(); }
signals: signals:
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
@ -138,9 +138,12 @@ private:
void resetEntitiesScriptEngine(); void resetEntitiesScriptEngine();
void addEntityToScene(EntityItemPointer entity); void addEntityToScene(EntityItemPointer entity);
bool findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar); bool findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar = nullptr);
bool applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
bool layerZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone);
bool applySkyboxAndHasAmbient();
void applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone);
void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false); void checkAndCallPreload(const EntityItemID& entityID, const bool reload = false);
QList<ModelPointer> _releasedModels; QList<ModelPointer> _releasedModels;
@ -156,15 +159,9 @@ private:
void leaveAllEntities(); void leaveAllEntities();
void forceRecheckEntities(); void forceRecheckEntities();
glm::vec3 _lastAvatarPosition { 0.0f }; glm::vec3 _avatarPosition { 0.0f };
QVector<EntityItemID> _currentEntitiesInside; QVector<EntityItemID> _currentEntitiesInside;
bool _pendingSkyboxTexture { false };
NetworkTexturePointer _skyboxTexture;
bool _pendingAmbientTexture { false };
NetworkTexturePointer _ambientTexture;
bool _wantScripts; bool _wantScripts;
QSharedPointer<ScriptEngine> _entitiesScriptEngine; QSharedPointer<ScriptEngine> _entitiesScriptEngine;
@ -185,26 +182,60 @@ private:
QMultiMap<QUrl, EntityItemID> _waitingOnPreload; QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
std::shared_ptr<ZoneEntityItem> _bestZone; class LayeredZone {
float _bestZoneVolume; public:
LayeredZone(std::shared_ptr<ZoneEntityItem> zone, QUuid id, float volume) : zone(zone), id(id), volume(volume) {}
LayeredZone(std::shared_ptr<ZoneEntityItem> zone) : LayeredZone(zone, zone->getID(), zone->getVolumeEstimate()) {}
bool operator<(const LayeredZone& r) const { return std::tie(volume, id) < std::tie(r.volume, r.id); }
bool operator==(const LayeredZone& r) const { return id == r.id; }
bool operator<=(const LayeredZone& r) const { return (*this < r) || (*this == r); }
std::shared_ptr<ZoneEntityItem> zone;
QUuid id;
float volume;
};
class LayeredZones : public std::set<LayeredZone> {
public:
LayeredZones(EntityTreeRenderer* parent) : _entityTreeRenderer(parent) {}
LayeredZones(LayeredZones&& other);
// avoid accidental misconstruction
LayeredZones() = delete;
LayeredZones(const LayeredZones&) = delete;
LayeredZones& operator=(const LayeredZones&) = delete;
LayeredZones& operator=(LayeredZones&&) = delete;
void clear();
std::pair<iterator, bool> insert(const LayeredZone& layer);
void apply();
void update(std::shared_ptr<ZoneEntityItem> zone);
bool contains(const LayeredZones& other);
std::shared_ptr<ZoneEntityItem> getZone() { return empty() ? nullptr : begin()->zone; }
private:
void applyPartial(iterator layer);
std::map<QUuid, iterator> _map;
iterator _skyboxLayer{ end() };
EntityTreeRenderer* _entityTreeRenderer;
};
LayeredZones _layeredZones;
QString _zoneUserData; QString _zoneUserData;
NetworkTexturePointer _skyboxTexture;
NetworkTexturePointer _ambientTexture;
bool _pendingSkyboxTexture { false };
bool _pendingAmbientTexture { false };
quint64 _lastZoneCheck { 0 }; quint64 _lastZoneCheck { 0 };
const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz
const float ZONE_CHECK_DISTANCE = 0.001f; const float ZONE_CHECK_DISTANCE = 0.001f;
glm::vec3 _previousKeyLightColor;
float _previousKeyLightIntensity;
float _previousKeyLightAmbientIntensity;
glm::vec3 _previousKeyLightDirection;
bool _previousStageSunModelEnabled;
float _previousStageLongitude;
float _previousStageLatitude;
float _previousStageAltitude;
float _previousStageHour;
int _previousStageDay;
QHash<EntityItemID, EntityItemPointer> _entitiesInScene; QHash<EntityItemID, EntityItemPointer> _entitiesInScene;
// For Scene.shouldRenderEntities // For Scene.shouldRenderEntities
QList<EntityItemID> _entityIDsLastInScene; QList<EntityItemID> _entityIDsLastInScene;