// // RenderableZoneEntityItem.cpp // // // Created by Clement on 4/22/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 "RenderableZoneEntityItem.h" #include #include #include #include #include #include #include #include #include "EntityTreeRenderer.h" // Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1 // is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down. static const float SPHERE_ENTITY_SCALE = 0.5f; using namespace render; using namespace render::entities; ZoneEntityRenderer::ZoneEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { _background->setSkybox(std::make_shared(entity->getCreated())); } void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) { if (_stage) { if (!LightStage::isIndexInvalid(_sunIndex)) { _stage->removeLight(_sunIndex); _sunIndex = INVALID_INDEX; } if (!LightStage::isIndexInvalid(_ambientIndex)) { _stage->removeLight(_ambientIndex); _ambientIndex = INVALID_INDEX; } } if (_backgroundStage) { if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) { _backgroundStage->removeBackground(_backgroundIndex); _backgroundIndex = INVALID_INDEX; } } if (_hazeStage) { if (!HazeStage::isIndexInvalid(_hazeIndex)) { _hazeStage->removeHaze(_hazeIndex); _hazeIndex = INVALID_INDEX; } } if (_bloomStage) { if (!BloomStage::isIndexInvalid(_bloomIndex)) { _bloomStage->removeBloom(_bloomIndex); _bloomIndex = INVALID_INDEX; } } } void ZoneEntityRenderer::doRender(RenderArgs* args) { // This is necessary so that zones can themselves be zone culled if (!passesZoneOcclusionTest(CullTest::_prevContainingZones)) { return; } if (!_stage) { _stage = args->_scene->getStage(); assert(_stage); } if (!_backgroundStage) { _backgroundStage = args->_scene->getStage(); assert(_backgroundStage); } if (!_hazeStage) { _hazeStage = args->_scene->getStage(); assert(_hazeStage); } if (!_bloomStage) { _bloomStage = args->_scene->getStage(); assert(_bloomStage); } { // Sun if (_needSunUpdate) { if (LightStage::isIndexInvalid(_sunIndex)) { _sunIndex = _stage->addLight(_sunLight); } else { _stage->updateLightArrayBuffer(_sunIndex); } _needSunUpdate = false; } } { // Ambient updateAmbientMap(); if (_needAmbientUpdate) { if (LightStage::isIndexInvalid(_ambientIndex)) { _ambientIndex = _stage->addLight(_ambientLight); } else { _stage->updateLightArrayBuffer(_ambientIndex); } _needAmbientUpdate = false; } } { // Skybox updateSkyboxMap(); if (_needBackgroundUpdate) { if (BackgroundStage::isIndexInvalid(_backgroundIndex)) { _backgroundIndex = _backgroundStage->addBackground(_background); } _needBackgroundUpdate = false; } } { if (_needHazeUpdate) { if (HazeStage::isIndexInvalid(_hazeIndex)) { _hazeIndex = _hazeStage->addHaze(_haze); } _needHazeUpdate = false; } } { if (_needBloomUpdate) { if (BloomStage::isIndexInvalid(_bloomIndex)) { _bloomIndex = _bloomStage->addBloom(_bloom); } _needBloomUpdate = false; } } if (_visible) { // Finally, push the lights visible in the frame // // If component is disabled then push component off state // else if component is enabled then push current state // (else mode is inherit, the value from the parent zone will be used // if (_keyLightMode == COMPONENT_MODE_DISABLED) { _stage->_currentFrame.pushSunLight(_stage->getSunOffLight()); } else if (_keyLightMode == COMPONENT_MODE_ENABLED) { _stage->_currentFrame.pushSunLight(_sunIndex); } if (_skyboxMode == COMPONENT_MODE_DISABLED) { _backgroundStage->_currentFrame.pushBackground(INVALID_INDEX); } else if (_skyboxMode == COMPONENT_MODE_ENABLED) { _backgroundStage->_currentFrame.pushBackground(_backgroundIndex); } if (_ambientLightMode == COMPONENT_MODE_DISABLED) { _stage->_currentFrame.pushAmbientLight(_stage->getAmbientOffLight()); } else if (_ambientLightMode == COMPONENT_MODE_ENABLED) { _stage->_currentFrame.pushAmbientLight(_ambientIndex); } // Haze only if the mode is not inherit, as the model deals with on/off if (_hazeMode != COMPONENT_MODE_INHERIT) { _hazeStage->_currentFrame.pushHaze(_hazeIndex); } if (_bloomMode == COMPONENT_MODE_DISABLED) { _bloomStage->_currentFrame.pushBloom(INVALID_INDEX); } else if (_bloomMode == COMPONENT_MODE_ENABLED) { _bloomStage->_currentFrame.pushBloom(_bloomIndex); } } CullTest::_containingZones.insert(_entityID); } void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { auto position = entity->getWorldPosition(); auto rotation = entity->getWorldOrientation(); auto dimensions = entity->getScaledDimensions(); bool rotationChanged = rotation != _lastRotation; bool transformChanged = rotationChanged || position != _lastPosition || dimensions != _lastDimensions; auto visible = entity->getVisible(); if (transformChanged || visible != _lastVisible) { _lastVisible = visible; DependencyManager::get()->updateZone(entity->getID()); } auto proceduralUserData = entity->getUserData(); bool proceduralUserDataChanged = _proceduralUserData != proceduralUserData; // FIXME one of the bools here could become true between being fetched and being reset, // resulting in a lost update bool keyLightChanged = entity->keyLightPropertiesChanged() || rotationChanged; bool ambientLightChanged = entity->ambientLightPropertiesChanged() || transformChanged; bool skyboxChanged = entity->skyboxPropertiesChanged() || proceduralUserDataChanged; bool hazeChanged = entity->hazePropertiesChanged(); bool bloomChanged = entity->bloomPropertiesChanged(); entity->resetRenderingPropertiesChanged(); if (transformChanged) { _lastPosition = entity->getWorldPosition(); _lastRotation = entity->getWorldOrientation(); _lastDimensions = entity->getScaledDimensions(); } if (proceduralUserDataChanged) { _proceduralUserData = entity->getUserData(); } updateKeyZoneItemFromEntity(entity); if (keyLightChanged) { _keyLightProperties = entity->getKeyLightProperties(); updateKeySunFromEntity(entity); } if (ambientLightChanged) { _ambientLightProperties = entity->getAmbientLightProperties(); updateAmbientLightFromEntity(entity); } if (skyboxChanged) { _skyboxProperties = entity->getSkyboxProperties(); updateKeyBackgroundFromEntity(entity); } if (hazeChanged) { _hazeProperties = entity->getHazeProperties(); updateHazeFromEntity(entity); } if (bloomChanged) { _bloomProperties = entity->getBloomProperties(); updateBloomFromEntity(entity); } bool visuallyReady = true; uint32_t skyboxMode = entity->getSkyboxMode(); if (skyboxMode == COMPONENT_MODE_ENABLED && !_skyboxTextureURL.isEmpty()) { bool skyboxLoadedOrFailed = (_skyboxTexture && (_skyboxTexture->isLoaded() || _skyboxTexture->isFailed())); visuallyReady = skyboxLoadedOrFailed; } entity->setVisuallyReady(visuallyReady); } void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { if (entity->getShapeType() == SHAPE_TYPE_SPHERE) { _renderTransform = getModelTransform(); _renderTransform.postScale(SPHERE_ENTITY_SCALE); } } ItemKey ZoneEntityRenderer::getKey() { return ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).build(); } bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (entity->getVisible() != _lastVisible) { return true; } if (entity->keyLightPropertiesChanged() || entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || entity->bloomPropertiesChanged() || entity->skyboxPropertiesChanged()) { return true; } if (_skyboxTextureURL != entity->getSkyboxProperties().getURL()) { return true; } if (entity->getWorldPosition() != _lastPosition) { return true; } if (entity->getScaledDimensions() != _lastDimensions) { return true; } if (entity->getWorldOrientation() != _lastRotation) { return true; } if (entity->getUserData() != _proceduralUserData) { return true; } // FIXME: do we need to trigger an update when shapeType changes? see doRenderUpdateAsynchronousTyped return false; } void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity) { setKeyLightMode((ComponentMode)entity->getKeyLightMode()); const auto& sunLight = editSunLight(); sunLight->setType(graphics::Light::SUN); sunLight->setPosition(_lastPosition); sunLight->setOrientation(_lastRotation); // Set the keylight sunLight->setColor(ColorUtils::toVec3(_keyLightProperties.getColor())); sunLight->setIntensity(_keyLightProperties.getIntensity()); sunLight->setDirection(_lastRotation * _keyLightProperties.getDirection()); sunLight->setCastShadows(_keyLightProperties.getCastShadows()); sunLight->setShadowBias(_keyLightProperties.getShadowBias()); sunLight->setShadowsMaxDistance(_keyLightProperties.getShadowMaxDistance()); } void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& entity) { setAmbientLightMode((ComponentMode)entity->getAmbientLightMode()); const auto& ambientLight = editAmbientLight(); ambientLight->setType(graphics::Light::AMBIENT); ambientLight->setPosition(_lastPosition); ambientLight->setOrientation(_lastRotation); // Set the ambient light ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity()); if (_ambientLightProperties.getAmbientURL().isEmpty()) { setAmbientURL(_skyboxProperties.getURL()); } else { setAmbientURL(_ambientLightProperties.getAmbientURL()); } ambientLight->setTransform(entity->getTransform().getInverseMatrix()); } void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) { setHazeMode((ComponentMode)entity->getHazeMode()); const auto& haze = editHaze(); const uint32_t hazeMode = entity->getHazeMode(); haze->setHazeActive(hazeMode == COMPONENT_MODE_ENABLED); haze->setAltitudeBased(_hazeProperties.getHazeAltitudeEffect()); haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange())); glm::u8vec3 hazeColor = _hazeProperties.getHazeColor(); haze->setHazeColor(toGlm(hazeColor)); glm::u8vec3 hazeGlareColor = _hazeProperties.getHazeGlareColor(); haze->setHazeGlareColor(toGlm(hazeGlareColor)); haze->setHazeEnableGlare(_hazeProperties.getHazeEnableGlare()); haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(_hazeProperties.getHazeGlareAngle())); float hazeAltitude = _hazeProperties.getHazeCeiling() - _hazeProperties.getHazeBaseRef(); haze->setHazeAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(hazeAltitude)); haze->setHazeBaseReference(_hazeProperties.getHazeBaseRef()); haze->setHazeBackgroundBlend(_hazeProperties.getHazeBackgroundBlend()); haze->setHazeAttenuateKeyLight(_hazeProperties.getHazeAttenuateKeyLight()); haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange())); haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude())); } void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) { setBloomMode((ComponentMode)entity->getBloomMode()); const auto& bloom = editBloom(); bloom->setBloomIntensity(_bloomProperties.getBloomIntensity()); bloom->setBloomThreshold(_bloomProperties.getBloomThreshold()); bloom->setBloomSize(_bloomProperties.getBloomSize()); } void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { setSkyboxMode((ComponentMode)entity->getSkyboxMode()); editBackground(); setSkyboxColor(toGlm(_skyboxProperties.getColor())); setProceduralUserData(_proceduralUserData); setSkyboxURL(_skyboxProperties.getURL()); } void ZoneEntityRenderer::updateKeyZoneItemFromEntity(const TypedEntityPointer& entity) { // Update rotation values editSkybox()->setOrientation(_lastRotation); /* TODO: Implement the sun model behavior / Keep this code here for reference, this is how we { // Set the stage bool isSunModelEnabled = this->getStageProperties().getSunModelEnabled(); sceneStage->setSunModelEnable(isSunModelEnabled); if (isSunModelEnabled) { sceneStage->setLocation(this->getStageProperties().getLongitude(), this->getStageProperties().getLatitude(), this->getStageProperties().getAltitude()); auto sceneTime = sceneStage->getTime(); sceneTime->setHour(this->getStageProperties().calculateHour()); sceneTime->setDay(this->getStageProperties().calculateDay()); } }*/ } void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) { if (_ambientTextureURL == ambientUrl) { return; } _ambientTextureURL = ambientUrl; if (_ambientTextureURL.isEmpty()) { _pendingAmbientTexture = false; _ambientTexture.clear(); _ambientLight->setAmbientMap(nullptr); _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY); } else { _pendingAmbientTexture = true; auto textureCache = DependencyManager::get(); _ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::AMBIENT_TEXTURE); } } void ZoneEntityRenderer::updateAmbientMap() { if (_pendingAmbientTexture) { if (_ambientTexture && _ambientTexture->isLoaded()) { _pendingAmbientTexture = false; auto texture = _ambientTexture->getGPUTexture(); if (texture) { if (texture->getIrradiance()) { _ambientLight->setAmbientSphere(*texture->getIrradiance()); } else { _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY); } editAmbientLight()->setAmbientMap(texture); } else { qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL(); } } } } void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) { if (_skyboxTextureURL == skyboxUrl) { return; } _skyboxTextureURL = skyboxUrl; if (_skyboxTextureURL.isEmpty()) { _pendingSkyboxTexture = false; _skyboxTexture.clear(); editSkybox()->setCubemap(nullptr); } else { _pendingSkyboxTexture = true; auto textureCache = DependencyManager::get(); _skyboxTexture = textureCache->getTexture(_skyboxTextureURL, image::TextureUsage::SKY_TEXTURE); } } void ZoneEntityRenderer::updateSkyboxMap() { if (_pendingSkyboxTexture) { if (_skyboxTexture && _skyboxTexture->isLoaded()) { _pendingSkyboxTexture = false; auto texture = _skyboxTexture->getGPUTexture(); if (texture) { editSkybox()->setCubemap(texture); } else { qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL(); } } } } void ZoneEntityRenderer::setHazeMode(ComponentMode mode) { _hazeMode = mode; } void ZoneEntityRenderer::setKeyLightMode(ComponentMode mode) { _keyLightMode = mode; } void ZoneEntityRenderer::setAmbientLightMode(ComponentMode mode) { _ambientLightMode = mode; } void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) { _skyboxMode = mode; } void ZoneEntityRenderer::setBloomMode(ComponentMode mode) { _bloomMode = mode; } void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) { editSkybox()->setColor(color); } void ZoneEntityRenderer::setProceduralUserData(const QString& userData) { std::dynamic_pointer_cast(editSkybox())->parse(userData); }