/* * ORXONOX - the hottest 3D action shooter ever to exist * > www.orxonox.net < * * * License notice: * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: * David 'davidsa' Salvisberg * Co-authors: * ... * */ /** @file LensFlare.cc @brief Implementation of the LensFlare class. */ #include "LensFlare.h" #include "core/XMLPort.h" #include "graphics/Billboard.h" #include "CameraManager.h" #include "RenderQueueListener.h" #include #include namespace orxonox { RegisterClass(LensFlare); LensFlare::LensFlare(Context* context) : StaticEntity(context), scale_(1.0f), fadeOnViewBorder_(true), fadeResolution_(7), fadeExponent_(2.0f) { RegisterObject(LensFlare); this->lensConfiguration_.emplace_back("lensflare/burst", 1.0f, 1.0f, 1.0f); //main burst this->lensConfiguration_.emplace_back("lensflare/burst", 0.7f, 1.2f, 1.05f); //secondary burst this->lensConfiguration_.emplace_back("lensflare/bursthalo", 0.7f, 0.9f, 1.0f); //halo around main burst this->lensConfiguration_.emplace_back("lensflare/ring", 0.1f, 2.5f, 0.9f); //all the different distanced lenses this->lensConfiguration_.emplace_back("lensflare/iris", 0.1f, 0.2f, 0.5f); this->lensConfiguration_.emplace_back("lensflare/halo5", 0.1f, 0.3f, 0.45f); this->lensConfiguration_.emplace_back("lensflare/halo5", 0.4f, 0.2f, 0.35f); this->lensConfiguration_.emplace_back("lensflare/iris", 0.1f, 0.4f, 0.25f); this->lensConfiguration_.emplace_back("lensflare/halo4", 0.05f, 0.2f, 0.2f); this->createBillboards(); this->registerVariables(); } LensFlare::~LensFlare() { } void LensFlare::XMLPort(Element& xmlelement, XMLPort::Mode mode) { SUPER(LensFlare, XMLPort, xmlelement, mode); XMLPortParam(LensFlare, "scale", setScale, getScale, xmlelement, mode).defaultValues(1.0f); XMLPortParam(LensFlare, "fadeOnViewBorder", setFadeOnViewBorder, isFadeOnViewBorder, xmlelement, mode).defaultValues(true); XMLPortParam(LensFlare, "fadeResolution", setFadeResolution, getFadeResolution, xmlelement, mode).defaultValues(7); XMLPortParam(LensFlare, "fadeExponent", setFadeExponent, getFadeExponent, xmlelement, mode).defaultValues(2.0f); XMLPortParam(LensFlare, "colour", setColour, getColour, xmlelement, mode); } void LensFlare::registerVariables() { registerVariable(this->scale_, VariableDirection::ToClient); registerVariable(this->fadeOnViewBorder_, VariableDirection::ToClient); registerVariable(this->fadeResolution_, VariableDirection::ToClient); } /** @brief This function creates all the billboards needed for the flare effect */ void LensFlare::createBillboards() { this->occlusionBillboard_ = new Billboard(this->getContext()); this->occlusionBillboard_->setMaterial("lensflare/hoq"); this->occlusionBillboard_->setVisible(false); this->occlusionBillboard_->disableFrustumCulling(); this->occlusionBillboard_->setRenderQueueGroup(RENDER_QUEUE_HOQ); this->attach(this->occlusionBillboard_); for (const LensFlare::Lens& lens : lensConfiguration_) { Billboard* lensPart = new Billboard(this->getContext()); lensPart->setMaterial(lens.material_); lensPart->disableFrustumCulling(); lensPart->setVisible(true); this->attach(lensPart); this->billboards_.push_back(lensPart); } } /** @brief This function updates the states of all the billboards, i.e. their positions, visibilty and dimensions @param viewDirection normalised vector pointing from the current camera to the point light center @param dimension the current dimension of the main billboard, we're always using square billboards @param lightIsVisible is the (point-)light source currently visible */ void LensFlare::updateBillboardStates(const Vector3& viewDirection, float dimension, bool lightIsVisible) { this->occlusionBillboard_->setDefaultDimensions(dimension * 0.5f, dimension * 0.5f); this->occlusionBillboard_->setVisible(lightIsVisible); int i = 0; for (Billboard* billboard : this->billboards_) { const LensFlare::Lens& lens = lensConfiguration_.at(i); billboard->setPosition(-viewDirection * (1.0f - lens.position_)); billboard->setVisible(lightIsVisible); billboard->setDefaultDimensions(dimension * lens.scale_, dimension * lens.scale_); i++; } } /** @brief This function updates the alpha values for all billboards except for the one used for Hardware Occlusion Querying @param alpha the new alpha value all visible billboards should use */ void LensFlare::updateBillboardAlphas(float alpha) { ColourValue cur = this->colour_; int i = 0; for (Billboard* billboard : this->billboards_) { cur.a = alpha * lensConfiguration_.at(i).alpha_; billboard->setColour(cur); i++; } } /** @brief This function generates point samples of the main burst billboard according to the fadeResolution and returns how many of them are in the view port @param dimension the current dimension of the main billboard, we're always using square billboards @return the absolute amount of point samples that are currently captured by the camera of the view port */ unsigned int LensFlare::getPointCount(float dimension) const { Ogre::Camera* camera = CameraManager::getInstance().getActiveCamera()->getOgreCamera(); Vector3 position = this->getWorldPosition(); Vector3 nX = camera->getDerivedOrientation().xAxis().normalisedCopy(); Vector3 nY = camera->getDerivedOrientation().yAxis().normalisedCopy(); int halfRes = fadeResolution_ / 2; float resDim = dimension / fadeResolution_; unsigned int count = 0; for (int i = -halfRes; i <= halfRes; i++) { for (int j = -halfRes; j <= halfRes; j++) { Vector3 point = position + (i*resDim) * nX + (j*resDim) * nY;//generate point samples if (camera->isVisible(point)) { count++; } } } return count; } void LensFlare::tick(float dt) { if(this->isVisible()) { // get the current distance of the lensflare center from the camera Ogre::Camera* camera = CameraManager::getInstance().getActiveCamera()->getOgreCamera(); //get active Ogre Camera Instance, so we can check whether the light source is visible float cameraDistance = camera->getDerivedPosition().distance(this->getPosition()); float dimension = cameraDistance * this->scale_; if (!this->fadeOnViewBorder_) { this->fadeResolution_ = 3;//this is so we can still determine when the billboard has left the screen } unsigned int pointCount = this->getPointCount(dimension * 0.25f * this->scale_); Vector3 viewDirection = this->getWorldPosition() - camera->getDerivedPosition() - camera->getDerivedDirection() * cameraDistance; this->updateBillboardStates(viewDirection, dimension, pointCount > 0); if (pointCount > 0) { Ogre::Sphere sphere(this->getPosition(), dimension * 0.25f * this->scale_); float left, right, top, bottom; camera->projectSphere(sphere, &left, &top, &right, &bottom);//approximate maximum pixel count of billboard with a sphere Ogre::RenderWindow* window = GraphicsManager::getInstance().getRenderWindow(); float maxCount = (right - left) * (top - bottom) * window->getWidth() * window->getHeight() * 0.25f; unsigned int pixelCount = this->getScene()->getRenderQueueListener()->getPixelCount();//get pixel count float ratio = (maxCount < 0.0f) ? 0.0f : (1.0f * pixelCount / maxCount);//prevent underflow and division by zero float borderRatio = 1.0f; if (this->fadeOnViewBorder_) { borderRatio = ((float) pointCount) / (((float) fadeResolution_) * ((float) fadeResolution_));//ratio for the border fade } //update alpha values of all billboards except the HOQ billboard this->updateBillboardAlphas(std::min(1.0f, std::pow(std::min(ratio, borderRatio), this->fadeExponent_))); } } } void LensFlare::changedVisibility() { } }