mirror of
https://github.com/lubosz/overte.git
synced 2025-04-25 01:23:57 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into fix/ambient-skybox
This commit is contained in:
commit
c8ff6ee4cc
10 changed files with 335 additions and 153 deletions
|
@ -52,7 +52,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
|
|||
_viewState(viewState),
|
||||
_scriptingServices(scriptingServices),
|
||||
_displayModelBounds(false),
|
||||
_dontDoPrecisionPicking(false)
|
||||
_dontDoPrecisionPicking(false),
|
||||
_layeredZones(this)
|
||||
{
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Model, RenderableModelEntityItem::factory)
|
||||
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
|
||||
|
@ -135,8 +136,8 @@ void EntityTreeRenderer::clear() {
|
|||
_entitiesInScene.clear();
|
||||
|
||||
// reset the zone to the default (while we load the next scene)
|
||||
_bestZone = nullptr;
|
||||
applyZonePropertiesToScene(_bestZone);
|
||||
_layeredZones.clear();
|
||||
applyZoneAndHasSkybox(nullptr);
|
||||
|
||||
OctreeRenderer::clear();
|
||||
}
|
||||
|
@ -192,10 +193,10 @@ void EntityTreeRenderer::update() {
|
|||
|
||||
// If we haven't already updated and previously attempted to load a texture,
|
||||
// check if the texture loaded and apply it
|
||||
if (!updated && (
|
||||
(_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())) ||
|
||||
(_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())))) {
|
||||
applyZonePropertiesToScene(_bestZone);
|
||||
if (!updated &&
|
||||
((_pendingAmbientTexture && (!_ambientTexture || _ambientTexture->isLoaded())) ||
|
||||
(_pendingSkyboxTexture && (!_skyboxTexture || _skyboxTexture->isLoaded())))) {
|
||||
applySkyboxAndHasAmbient();
|
||||
}
|
||||
|
||||
// Even if we're not moving the mouse, if we started clicking on an entity and we have
|
||||
|
@ -210,7 +211,7 @@ void EntityTreeRenderer::update() {
|
|||
deleteReleasedModels();
|
||||
}
|
||||
|
||||
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3& avatarPosition, QVector<EntityItemID>* entitiesContainingAvatar) {
|
||||
bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(QVector<EntityItemID>* entitiesContainingAvatar) {
|
||||
bool didUpdate = false;
|
||||
float radius = 0.01f; // for now, assume 0.01 meter radius, because we actually check the point inside later
|
||||
QVector<EntityItemPointer> foundEntities;
|
||||
|
@ -220,12 +221,10 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
|
|||
_tree->withReadLock([&] {
|
||||
|
||||
// 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.
|
||||
auto oldBestZone = _bestZone;
|
||||
_bestZone = nullptr; // NOTE: Is this what we want?
|
||||
_bestZoneVolume = std::numeric_limits<float>::max();
|
||||
LayeredZones oldLayeredZones(std::move(_layeredZones));
|
||||
_layeredZones.clear();
|
||||
|
||||
// create a list of entities that actually contain the avatar's position
|
||||
for (auto& entity : foundEntities) {
|
||||
|
@ -239,38 +238,34 @@ bool EntityTreeRenderer::findBestZoneAndMaybeContainingEntities(const glm::vec3&
|
|||
if (isZone || hasScript) {
|
||||
// now check to see if the point contains our entity, this can be expensive if
|
||||
// the entity has a collision hull
|
||||
if (entity->contains(avatarPosition)) {
|
||||
if (entity->contains(_avatarPosition)) {
|
||||
if (entitiesContainingAvatar) {
|
||||
*entitiesContainingAvatar << entity->getEntityItemID();
|
||||
}
|
||||
|
||||
// if this entity is a zone and visible, determine if it is the bestZone
|
||||
if (isZone && entity->getVisible()) {
|
||||
float entityVolumeEstimate = entity->getVolumeEstimate();
|
||||
if (entityVolumeEstimate < _bestZoneVolume) {
|
||||
_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);
|
||||
}
|
||||
}
|
||||
auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(entity);
|
||||
_layeredZones.insert(zone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_bestZone != oldBestZone) {
|
||||
applyZonePropertiesToScene(_bestZone);
|
||||
didUpdate = true;
|
||||
// check if our layered zones have changed
|
||||
if (_layeredZones.empty()) {
|
||||
if (oldLayeredZones.empty()) {
|
||||
return;
|
||||
}
|
||||
} else if (!oldLayeredZones.empty()) {
|
||||
if (_layeredZones.contains(oldLayeredZones)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_layeredZones.apply();
|
||||
didUpdate = true;
|
||||
});
|
||||
|
||||
return didUpdate;
|
||||
}
|
||||
|
||||
|
@ -286,13 +281,14 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
// 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
|
||||
// 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;
|
||||
|
||||
if (movedEnough || enoughTimeElapsed) {
|
||||
_avatarPosition = avatarPosition;
|
||||
_lastZoneCheck = now;
|
||||
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
|
||||
// EntityItemIDs from here. The callEntityScriptMethod() method is robust against attempting to call scripts
|
||||
|
@ -318,7 +314,6 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() {
|
|||
}
|
||||
}
|
||||
_currentEntitiesInside = entitiesContainingAvatar;
|
||||
_lastAvatarPosition = avatarPosition;
|
||||
}
|
||||
}
|
||||
return didUpdate;
|
||||
|
@ -342,24 +337,20 @@ void EntityTreeRenderer::leaveAllEntities() {
|
|||
void EntityTreeRenderer::forceRecheckEntities() {
|
||||
// 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.
|
||||
_lastAvatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
|
||||
_avatarPosition = _viewState->getAvatarPosition() + glm::vec3((float)TREE_SCALE);
|
||||
}
|
||||
|
||||
|
||||
void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityItem> zone) {
|
||||
bool EntityTreeRenderer::applyZoneAndHasSkybox(const std::shared_ptr<ZoneEntityItem>& zone) {
|
||||
auto textureCache = DependencyManager::get<TextureCache>();
|
||||
auto scene = DependencyManager::get<SceneScriptingInterface>();
|
||||
auto sceneStage = scene->getStage();
|
||||
auto skyStage = scene->getSkyStage();
|
||||
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 (!zone) {
|
||||
_zoneUserData = QString();
|
||||
skybox->clear();
|
||||
skyStage->getSkybox()->clear();
|
||||
|
||||
_pendingSkyboxTexture = false;
|
||||
_skyboxTexture.clear();
|
||||
|
@ -371,7 +362,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
|
|||
sceneKeyLight->setAmbientMap(nullptr);
|
||||
|
||||
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the keylight
|
||||
|
@ -394,90 +385,127 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptr<ZoneEntityIt
|
|||
}
|
||||
|
||||
// Set the ambient texture
|
||||
bool isAmbientTextureSet = false;
|
||||
if (zone->getKeyLightProperties().getAmbientURL().isEmpty()) {
|
||||
_ambientTextureURL = zone->getKeyLightProperties().getAmbientURL();
|
||||
if (_ambientTextureURL.isEmpty()) {
|
||||
_pendingAmbientTexture = false;
|
||||
_ambientTexture.clear();
|
||||
} else {
|
||||
_ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), NetworkTexture::CUBE_TEXTURE);
|
||||
_pendingAmbientTexture = true;
|
||||
|
||||
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
|
||||
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()) {
|
||||
case BACKGROUND_MODE_SKYBOX: {
|
||||
case BACKGROUND_MODE_SKYBOX:
|
||||
hasSkybox = true;
|
||||
|
||||
skybox->setColor(zone->getSkyboxProperties().getColorVec3());
|
||||
|
||||
if (_zoneUserData != zone->getUserData()) {
|
||||
_zoneUserData = zone->getUserData();
|
||||
skybox->parse(_zoneUserData);
|
||||
std::dynamic_pointer_cast<ProceduralSkybox>(skybox)->parse(_zoneUserData);
|
||||
}
|
||||
if (zone->getSkyboxProperties().getURL().isEmpty()) {
|
||||
skybox->setCubemap(nullptr);
|
||||
|
||||
_skyboxTextureURL = zone->getSkyboxProperties().getURL();
|
||||
if (_skyboxTextureURL.isEmpty()) {
|
||||
_pendingSkyboxTexture = false;
|
||||
_skyboxTexture.clear();
|
||||
} else {
|
||||
// Update the Texture of the Skybox with the one pointed by this zone
|
||||
_skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), NetworkTexture::CUBE_TEXTURE);
|
||||
_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);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case BACKGROUND_MODE_INHERIT:
|
||||
default:
|
||||
// Clear the skybox to release its textures
|
||||
_zoneUserData = QString();
|
||||
skybox->clear();
|
||||
_zoneUserData = QString();
|
||||
|
||||
_skyboxTexture.clear();
|
||||
_pendingSkyboxTexture = false;
|
||||
_skyboxTexture.clear();
|
||||
|
||||
// Let the application background through
|
||||
if (isAmbientTextureSet) {
|
||||
if (applySkyboxAndHasAmbient()) {
|
||||
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_TEXTURE);
|
||||
} else {
|
||||
skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE);
|
||||
}
|
||||
|
||||
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 (_pendingAmbientTexture && !_ambientTexture) {
|
||||
_ambientTexture = textureCache->getTexture(_ambientTextureURL, NetworkTexture::CUBE_TEXTURE);
|
||||
}
|
||||
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 (_pendingSkyboxTexture && !_skyboxTexture) {
|
||||
_skyboxTexture = textureCache->getTexture(_skyboxTextureURL, NetworkTexture::CUBE_TEXTURE);
|
||||
}
|
||||
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->setAmbientMap(nullptr);
|
||||
}
|
||||
|
||||
return isAmbientSet;
|
||||
}
|
||||
|
||||
const FBXGeometry* EntityTreeRenderer::getGeometryForEntity(EntityItemPointer entityItem) {
|
||||
|
@ -1046,21 +1074,128 @@ void EntityTreeRenderer::updateEntityRenderStatus(bool shouldRenderEntities) {
|
|||
}
|
||||
|
||||
void EntityTreeRenderer::updateZone(const EntityItemID& id) {
|
||||
if (!_bestZone) {
|
||||
// Get in the zone!
|
||||
auto zone = getTree()->findEntityByEntityItemID(id);
|
||||
if (zone && zone->contains(_lastAvatarPosition)) {
|
||||
_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);
|
||||
// Get in the zone!
|
||||
auto zone = std::dynamic_pointer_cast<ZoneEntityItem>(getTree()->findEntityByEntityItemID(id));
|
||||
if (zone && zone->contains(_avatarPosition)) {
|
||||
_layeredZones.update(zone);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (layer == end()) {
|
||||
if (empty()) {
|
||||
_entityTreeRenderer->applyZoneAndHasSkybox(nullptr);
|
||||
return;
|
||||
} else { // a layer was removed - reapply from beginning
|
||||
layer = begin();
|
||||
}
|
||||
}
|
||||
|
||||
if (layer == begin()) {
|
||||
hasSkybox = _entityTreeRenderer->applyZoneAndHasSkybox(layer->zone);
|
||||
} else {
|
||||
hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
|
||||
}
|
||||
|
||||
if (layer != end()) {
|
||||
while (!hasSkybox && ++layer != end()) {
|
||||
hasSkybox = _entityTreeRenderer->layerZoneAndHasSkybox(layer->zone);
|
||||
}
|
||||
}
|
||||
|
||||
_skyboxLayer = layer;
|
||||
}
|
||||
|
||||
bool EntityTreeRenderer::LayeredZones::contains(const LayeredZones& other) {
|
||||
bool result = std::equal(other.begin(), other._skyboxLayer, begin());
|
||||
if (result) {
|
||||
// if valid, set the _skyboxLayer from the other LayeredZones
|
||||
_skyboxLayer = std::next(begin(), std::distance(other.begin(), other._skyboxLayer));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ public:
|
|||
// For Scene.shouldRenderEntities
|
||||
QList<EntityItemID>& getEntitiesLastInScene() { return _entityIDsLastInScene; }
|
||||
|
||||
std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _bestZone; }
|
||||
std::shared_ptr<ZoneEntityItem> myAvatarZone() { return _layeredZones.getZone(); }
|
||||
|
||||
signals:
|
||||
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
|
||||
|
@ -138,9 +138,12 @@ private:
|
|||
void resetEntitiesScriptEngine();
|
||||
|
||||
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);
|
||||
|
||||
QList<ModelPointer> _releasedModels;
|
||||
|
@ -156,15 +159,9 @@ private:
|
|||
void leaveAllEntities();
|
||||
void forceRecheckEntities();
|
||||
|
||||
glm::vec3 _lastAvatarPosition { 0.0f };
|
||||
glm::vec3 _avatarPosition { 0.0f };
|
||||
QVector<EntityItemID> _currentEntitiesInside;
|
||||
|
||||
bool _pendingSkyboxTexture { false };
|
||||
NetworkTexturePointer _skyboxTexture;
|
||||
|
||||
bool _pendingAmbientTexture { false };
|
||||
NetworkTexturePointer _ambientTexture;
|
||||
|
||||
bool _wantScripts;
|
||||
QSharedPointer<ScriptEngine> _entitiesScriptEngine;
|
||||
|
||||
|
@ -185,26 +182,62 @@ private:
|
|||
|
||||
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
|
||||
|
||||
std::shared_ptr<ZoneEntityItem> _bestZone;
|
||||
float _bestZoneVolume;
|
||||
class LayeredZone {
|
||||
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;
|
||||
NetworkTexturePointer _ambientTexture;
|
||||
NetworkTexturePointer _skyboxTexture;
|
||||
QString _ambientTextureURL;
|
||||
QString _skyboxTextureURL;
|
||||
bool _pendingAmbientTexture { false };
|
||||
bool _pendingSkyboxTexture { false };
|
||||
|
||||
quint64 _lastZoneCheck { 0 };
|
||||
const quint64 ZONE_CHECK_INTERVAL = USECS_PER_MSEC * 100; // ~10hz
|
||||
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;
|
||||
// For Scene.shouldRenderEntities
|
||||
QList<EntityItemID> _entityIDsLastInScene;
|
||||
|
|
|
@ -26,13 +26,15 @@ Skybox::Skybox() {
|
|||
}
|
||||
|
||||
void Skybox::setColor(const Color& color) {
|
||||
_empty = false;
|
||||
_schemaBuffer.edit<Schema>().color = color;
|
||||
_empty = false;
|
||||
}
|
||||
|
||||
void Skybox::setCubemap(const gpu::TexturePointer& cubemap) {
|
||||
_empty = false;
|
||||
_cubemap = cubemap;
|
||||
if (cubemap) {
|
||||
_empty = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Skybox::updateSchemaBuffer() const {
|
||||
|
@ -52,9 +54,9 @@ void Skybox::updateSchemaBuffer() const {
|
|||
}
|
||||
|
||||
void Skybox::clear() {
|
||||
_empty = true;
|
||||
_schemaBuffer.edit<Schema>().color = vec3(0);
|
||||
setCubemap(nullptr);
|
||||
_cubemap = nullptr;
|
||||
_empty = true;
|
||||
}
|
||||
|
||||
void Skybox::prepare(gpu::Batch& batch, int textureSlot, int bufferSlot) const {
|
||||
|
|
|
@ -100,7 +100,9 @@ bool Procedural::parseVersion(const QJsonValue& version) {
|
|||
return (_version == 1 || _version == 2);
|
||||
}
|
||||
|
||||
bool Procedural::parseUrl(const QUrl& shaderUrl) {
|
||||
bool Procedural::parseShader(const QUrl& shaderPath) {
|
||||
auto shaderUrl = ResourceManager::normalizeURL(shaderPath);
|
||||
|
||||
if (!shaderUrl.isValid()) {
|
||||
if (!shaderUrl.isEmpty()) {
|
||||
qWarning() << "Invalid shader URL: " << shaderUrl;
|
||||
|
@ -168,7 +170,6 @@ void Procedural::parse(const QJsonObject& proceduralData) {
|
|||
|
||||
auto version = proceduralData[VERSION_KEY];
|
||||
auto shaderUrl = proceduralData[URL_KEY].toString();
|
||||
shaderUrl = ResourceManager::normalizeURL(shaderUrl);
|
||||
auto uniforms = proceduralData[UNIFORMS_KEY].toObject();
|
||||
auto channels = proceduralData[CHANNELS_KEY].toArray();
|
||||
|
||||
|
@ -176,7 +177,7 @@ void Procedural::parse(const QJsonObject& proceduralData) {
|
|||
|
||||
// Run through parsing regardless of validity to clear old cached resources
|
||||
isValid = parseVersion(version) && isValid;
|
||||
isValid = parseUrl(shaderUrl) && isValid;
|
||||
isValid = parseShader(shaderUrl) && isValid;
|
||||
isValid = parseUniforms(uniforms) && isValid;
|
||||
isValid = parseTextures(channels) && isValid;
|
||||
|
||||
|
@ -221,6 +222,7 @@ bool Procedural::ready() {
|
|||
_hasStartedFade = true;
|
||||
_isFading = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ private:
|
|||
// This should only be called from the render thread, as it shares data with Procedural::prepare
|
||||
void parse(const QJsonObject&);
|
||||
bool parseVersion(const QJsonValue& version);
|
||||
bool parseUrl(const QUrl& url);
|
||||
bool parseShader(const QUrl& shaderPath);
|
||||
bool parseUniforms(const QJsonObject& uniforms);
|
||||
bool parseTextures(const QJsonArray& channels);
|
||||
|
||||
|
|
|
@ -164,9 +164,10 @@ function toggleMarketplace() {
|
|||
}
|
||||
}
|
||||
|
||||
var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
|
||||
|
||||
var toolBar = (function () {
|
||||
var EDIT_SETTING = "io.highfidelity.isEditting"; // for communication with other scripts
|
||||
var TOOL_ICON_URL = Script.resolvePath("assets/images/tools/");
|
||||
var that = {},
|
||||
toolBar,
|
||||
systemToolbar,
|
||||
|
@ -199,7 +200,7 @@ var toolBar = (function () {
|
|||
}
|
||||
|
||||
function addButton(name, image, handler) {
|
||||
var imageUrl = TOOL_ICON_URL + image;
|
||||
var imageUrl = TOOLS_PATH + image;
|
||||
var button = toolBar.addButton({
|
||||
objectName: name,
|
||||
imageURL: imageUrl,
|
||||
|
@ -232,7 +233,7 @@ var toolBar = (function () {
|
|||
systemToolbar = Toolbars.getToolbar(SYSTEM_TOOLBAR);
|
||||
activeButton = systemToolbar.addButton({
|
||||
objectName: EDIT_TOGGLE_BUTTON,
|
||||
imageURL: TOOL_ICON_URL + "edit.svg",
|
||||
imageURL: TOOLS_PATH + "edit.svg",
|
||||
visible: true,
|
||||
alpha: 0.9,
|
||||
buttonState: 1,
|
||||
|
@ -1326,13 +1327,14 @@ function pushCommandForSelections(createdEntityData, deletedEntityData) {
|
|||
UndoStack.pushCommand(applyEntityProperties, undoData, applyEntityProperties, redoData);
|
||||
}
|
||||
|
||||
var ENTITY_PROPERTIES_URL = Script.resolvePath('html/entityProperties.html');
|
||||
|
||||
var PropertiesTool = function (opts) {
|
||||
var that = {};
|
||||
|
||||
var url = Script.resolvePath('html/entityProperties.html');
|
||||
var webView = new OverlayWebWindow({
|
||||
title: 'Entity Properties',
|
||||
source: url,
|
||||
source: ENTITY_PROPERTIES_URL,
|
||||
toolWindow: true
|
||||
});
|
||||
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
// grab the toolbar
|
||||
var toolbar = Toolbars.getToolbar("com.highfidelity.interface.toolbar.system");
|
||||
|
||||
var ASSETS_PATH = Script.resolvePath("assets");
|
||||
var TOOLS_PATH = Script.resolvePath("assets/images/tools/");
|
||||
|
||||
function buttonImageURL() {
|
||||
return Script.resolvePath("assets/images/tools/" + (Users.canKick ? 'kick.svg' : 'ignore.svg'));
|
||||
return TOOLS_PATH + (Users.canKick ? 'kick.svg' : 'ignore.svg');
|
||||
}
|
||||
|
||||
// setup the mod button and add it to the toolbar
|
||||
|
@ -68,7 +71,7 @@ function buttonClicked(){
|
|||
button.clicked.connect(buttonClicked);
|
||||
|
||||
function overlayURL() {
|
||||
return Script.resolvePath("assets") + "/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg");
|
||||
return ASSETS_PATH + "/images/" + (Users.canKick ? "kick-target.svg" : "ignore-target.svg");
|
||||
}
|
||||
|
||||
function updateOverlays() {
|
||||
|
|
|
@ -124,7 +124,6 @@ var NotificationType = {
|
|||
var randomSounds = new SoundArray({ localOnly: true }, true);
|
||||
var numberOfSounds = 2;
|
||||
for (var i = 1; i <= numberOfSounds; i++) {
|
||||
|
||||
randomSounds.addSound(Script.resolvePath("assets/sounds/notification-general"+ i + ".raw"));
|
||||
}
|
||||
|
||||
|
@ -317,6 +316,8 @@ function notify(notice, button, height, imageProperties, image) {
|
|||
return notificationText;
|
||||
}
|
||||
|
||||
var CLOSE_NOTIFICATION_ICON = Script.resolvePath("assets/images/close-small-light.svg");
|
||||
|
||||
// This function creates and sizes the overlays
|
||||
function createNotification(text, notificationType, imageProperties) {
|
||||
var count = (text.match(/\n/g) || []).length,
|
||||
|
@ -363,7 +364,7 @@ function createNotification(text, notificationType, imageProperties) {
|
|||
width: 10.0,
|
||||
height: 10.0,
|
||||
subImage: { x: 0, y: 0, width: 10, height: 10 },
|
||||
imageURL: Script.resolvePath("assets/images/close-small-light.svg"),
|
||||
imageURL: CLOSE_NOTIFICATION_ICON,
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
visible: true,
|
||||
alpha: backgroundAlpha
|
||||
|
@ -534,7 +535,7 @@ function onDomainConnectionRefused(reason) {
|
|||
function onSnapshotTaken(path, notify) {
|
||||
if (notify) {
|
||||
var imageProperties = {
|
||||
path: Script.resolvePath("file:///" + path),
|
||||
path: "file:///" + path,
|
||||
aspectRatio: Window.innerWidth / Window.innerHeight
|
||||
}
|
||||
createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties);
|
||||
|
|
|
@ -32,9 +32,11 @@ function showFeedWindow() {
|
|||
DialogsManager.showFeed();
|
||||
}
|
||||
|
||||
var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html");
|
||||
|
||||
var outstanding;
|
||||
function confirmShare(data) {
|
||||
var dialog = new OverlayWebWindow('Snapshot Review', Script.resolvePath("html/SnapshotReview.html"), 800, 320);
|
||||
var dialog = new OverlayWebWindow('Snapshot Review', SNAPSHOT_REVIEW_URL, 800, 320);
|
||||
function onMessage(message) {
|
||||
// Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following:
|
||||
// 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)
|
||||
|
|
|
@ -13,6 +13,10 @@
|
|||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
||||
// resolve these paths immediately
|
||||
var MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg");
|
||||
var BASE_URL = Script.resolvePath("assets/images/tools/");
|
||||
|
||||
var PopUpMenu = function (properties) {
|
||||
var value = properties.value,
|
||||
promptOverlay,
|
||||
|
@ -25,8 +29,7 @@ var PopUpMenu = function (properties) {
|
|||
MIN_MAX_BUTTON_SVG_WIDTH = 17.1,
|
||||
MIN_MAX_BUTTON_SVG_HEIGHT = 32.5,
|
||||
MIN_MAX_BUTTON_WIDTH = 14,
|
||||
MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH,
|
||||
MIN_MAX_BUTTON_SVG = Script.resolvePath("assets/images/tools/min-max-toggle.svg");
|
||||
MIN_MAX_BUTTON_HEIGHT = MIN_MAX_BUTTON_WIDTH;
|
||||
|
||||
function positionDisplayOptions() {
|
||||
var y,
|
||||
|
@ -223,8 +226,7 @@ var PopUpMenu = function (properties) {
|
|||
|
||||
var usersWindow = (function () {
|
||||
|
||||
var baseURL = Script.resolvePath("assets/images/tools/"),
|
||||
WINDOW_WIDTH = 260,
|
||||
var WINDOW_WIDTH = 260,
|
||||
WINDOW_MARGIN = 12,
|
||||
WINDOW_BASE_MARGIN = 6, // A little less is needed in order look correct
|
||||
WINDOW_FONT = {
|
||||
|
@ -261,7 +263,7 @@ var usersWindow = (function () {
|
|||
WINDOW_BORDER_ALPHA = 0.5,
|
||||
windowBorder,
|
||||
|
||||
MIN_MAX_BUTTON_SVG = baseURL + "min-max-toggle.svg",
|
||||
MIN_MAX_BUTTON_SVG = BASE_URL + "min-max-toggle.svg",
|
||||
MIN_MAX_BUTTON_SVG_WIDTH = 17.1,
|
||||
MIN_MAX_BUTTON_SVG_HEIGHT = 32.5,
|
||||
MIN_MAX_BUTTON_WIDTH = 14,
|
||||
|
@ -293,7 +295,7 @@ var usersWindow = (function () {
|
|||
scrollbarBackgroundHeight,
|
||||
scrollbarBarHeight,
|
||||
FRIENDS_BUTTON_SPACER = 6, // Space before add/remove friends button
|
||||
FRIENDS_BUTTON_SVG = baseURL + "add-remove-friends.svg",
|
||||
FRIENDS_BUTTON_SVG = BASE_URL + "add-remove-friends.svg",
|
||||
FRIENDS_BUTTON_SVG_WIDTH = 107,
|
||||
FRIENDS_BUTTON_SVG_HEIGHT = 27,
|
||||
FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH,
|
||||
|
|
Loading…
Reference in a new issue