/* This file is part of Caelum. See http://www.ogre3d.org/wiki/index.php/Caelum Copyright (c) 2008 Caelum team. See Contributors.txt for details. Caelum is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Caelum 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with Caelum. If not, see . */ #include "CaelumPrecompiled.h" #include "PointStarfield.h" #include "CaelumExceptions.h" #include "Astronomy.h" #include "InternalUtilities.h" using namespace Ogre; namespace Caelum { const Ogre::String PointStarfield::STARFIELD_MATERIAL_NAME = "Caelum/StarPoint"; const Ogre::Degree PointStarfield::DEFAULT_OBSERVER_POSITION_REBUILD_DELTA = Ogre::Degree(0.1); PointStarfield::PointStarfield ( Ogre::SceneManager *sceneMgr, Ogre::SceneNode *caelumRootNode, bool initWithCatalogue) { mMag0PixelSize = 16; mMinPixelSize = 4; mMaxPixelSize = 6; mMagnitudeScale = Math::Pow(100, 0.2); mObserverLatitude = 45; mObserverLongitude = 0; mObserverPositionRebuildDelta = DEFAULT_OBSERVER_POSITION_REBUILD_DELTA; String uniqueSuffix = "/" + InternalUtilities::pointerToString(this); // Load material. mMaterial.reset(InternalUtilities::checkLoadMaterialClone( STARFIELD_MATERIAL_NAME, STARFIELD_MATERIAL_NAME + uniqueSuffix)); mParams.setup(mMaterial->getTechnique(0)->getPass(0)->getVertexProgramParameters()); // We use a separate data source. Ogre::String objName = "Caelum/PointStarfield" + uniqueSuffix; mManualObj.reset (sceneMgr->createManualObject (objName)); mManualObj->setDynamic(false); mManualObj->setRenderQueueGroup (CAELUM_RENDER_QUEUE_STARFIELD); sceneMgr->getRenderQueue()->getQueueGroup(CAELUM_RENDER_QUEUE_STARFIELD)->setShadowsEnabled (false); mManualObj->setCastShadows(false); mNode.reset (caelumRootNode->createChildSceneNode ()); mNode->attachObject (mManualObj.getPointer ()); if (initWithCatalogue) { addBrightStarCatalogue (); } } PointStarfield::~PointStarfield () { } void PointStarfield::notifyStarVectorChanged () { invalidateGeometry (); } void PointStarfield::clearAllStars () { mStars.clear(); notifyStarVectorChanged (); } Real randReal () { return rand() / static_cast(RAND_MAX); } Real randReal (Real min, Real max) { Real f = randReal (); return min * (1 - f) + max * f; } void PointStarfield::addRandomStars (int count) { for (int i = 0; i < count; ++i) { // Generate a vector inside a sphere Ogre::Vector3 pos; do { pos.x = randReal(-1, 1); pos.y = randReal(-1, 1); pos.z = randReal(-1, 1); } while (pos.squaredLength () >= 1); // Convert to rasc/decl angles. LongReal rasc, decl, dist; Astronomy::convertRectangularToSpherical( pos.x, pos.y, pos.z, rasc, decl, dist); Star s; s.RightAscension = Ogre::Degree (rasc); s.Declination = Ogre::Degree (decl); // This distribution is wrong. s.Magnitude = 6 * pos.squaredLength () + 1.5; mStars.push_back(s); } notifyStarVectorChanged (); } void PointStarfield::addStar (const BrightStarCatalogueEntry &entry) { Star s; s.RightAscension = Ogre::Degree(360 / 24.0f * ( Math::Abs(entry.rasc_hour) + entry.rasc_min / 60.0f + entry.rasc_sec / 3600.0f)); s.Declination = Ogre::Degree(Math::Sign(entry.decl_deg) * ( Math::Abs(entry.decl_deg) + entry.decl_min / 60.0f + entry.decl_sec / 3600.0f)); s.Magnitude = entry.magn; mStars.push_back(s); notifyStarVectorChanged (); } void PointStarfield::addBrightStarCatalogue (int count) { assert(count >= 0); if (count < BrightStarCatalogueSize) { // Only sort if we don't add everything. // It would be lovely if the catalogue was already sorted. std::vector > vec; vec.reserve(BrightStarCatalogueSize); for (int i = 0; i < BrightStarCatalogueSize; ++i) { vec.push_back(std::make_pair(BrightStarCatalogue[i].magn, i)); } sort(vec.begin(), vec.end()); for (int i = 0; i < count; ++i) { addStar(BrightStarCatalogue[vec[i].second]); } } else { assert(count == BrightStarCatalogueSize); for (int i = 0; i < BrightStarCatalogueSize; ++i) { addStar(BrightStarCatalogue[i]); } } notifyStarVectorChanged (); } void PointStarfield::invalidateGeometry () { mValidGeometry = false; } void PointStarfield::ensureGeometry () { if (mValidGeometry) { return; } //Ogre::LogManager::getSingleton ().logMessage ("Caelum: Recomputing starfield geometry."); size_t starCount = mStars.size(); mManualObj->clear(); mManualObj->estimateVertexCount(6 * starCount); mManualObj->begin(mMaterial->getName (), Ogre::RenderOperation::OT_TRIANGLE_LIST); for (uint i = 0; i < starCount; ++i) { const Star& star = mStars[i]; // Determine position at J2000 LongReal azm, alt; Astronomy::convertEquatorialToHorizontal( Astronomy::J2000, mObserverLatitude.valueDegrees(), mObserverLongitude.valueDegrees(), star.RightAscension.valueDegrees(), star.Declination.valueDegrees(), azm, alt); Ogre::Vector3 pos; pos.z = -Math::Cos (Ogre::Degree(azm)) * Math::Cos (Ogre::Degree(alt)); pos.x = Math::Sin (Ogre::Degree(azm)) * Math::Cos (Ogre::Degree(alt)); pos.y = -Math::Sin (Ogre::Degree(alt)); //mManualObj->colour (Ogre::ColourValue::White); mManualObj->position (pos); mManualObj->textureCoord (+1, -1, star.Magnitude); mManualObj->position (pos); mManualObj->textureCoord (+1, +1, star.Magnitude); mManualObj->position (pos); mManualObj->textureCoord (-1, -1, star.Magnitude); mManualObj->position (pos); mManualObj->textureCoord (-1, -1, star.Magnitude); mManualObj->position (pos); mManualObj->textureCoord (+1, +1, star.Magnitude); mManualObj->position (pos); mManualObj->textureCoord (-1, +1, star.Magnitude); } mManualObj->end(); // Set infinite bounds on the starfield. AxisAlignedBox box; box.setInfinite (); mManualObj->setBoundingBox (box); mValidGeometry = true; } void PointStarfield::Params::setup(Ogre::GpuProgramParametersSharedPtr vpParams) { this->vpParams = vpParams; this->mag_scale.bind(vpParams, "mag_scale"); this->mag0_size.bind(vpParams, "mag0_size"); this->min_size.bind(vpParams, "min_size"); this->max_size.bind(vpParams, "max_size"); this->aspect_ratio.bind(vpParams, "aspect_ratio"); } void PointStarfield::notifyCameraChanged (Ogre::Camera *cam) { CameraBoundElement::notifyCameraChanged (cam); // Shader params are changed for every camera. Pass* pass = mMaterial->getBestTechnique ()->getPass (0); GpuProgramParametersSharedPtr fpParams = pass->getFragmentProgramParameters (); GpuProgramParametersSharedPtr vpParams = pass->getVertexProgramParameters (); int height = cam->getViewport ()-> getActualHeight (); int width = cam->getViewport ()-> getActualWidth (); Real pixFactor = 1.0f / width; Real magScale = -Math::Log (mMagnitudeScale) / 2; Real mag0Size = mMag0PixelSize * pixFactor; Real minSize = mMinPixelSize * pixFactor; Real maxSize = mMaxPixelSize * pixFactor; Real aspectRatio = static_cast(width) / height; // These params are relative to the size of the screen. mParams.mag_scale.set(mParams.vpParams, magScale); mParams.mag0_size.set(mParams.vpParams, mag0Size); mParams.min_size.set(mParams.vpParams, minSize); mParams.max_size.set(mParams.vpParams, maxSize); mParams.aspect_ratio.set(mParams.vpParams, aspectRatio); } void PointStarfield::setFarRadius (Ogre::Real radius) { CameraBoundElement::setFarRadius(radius); mNode->setScale (Ogre::Vector3::UNIT_SCALE * radius); } void PointStarfield::_update (const float time) { // This is probably wrong. Ogre::Quaternion orientation = Ogre::Quaternion::IDENTITY; orientation = orientation * Ogre::Quaternion (Ogre::Radian (-mObserverLatitude + Ogre::Degree (90)), Ogre::Vector3::UNIT_X); orientation = orientation * Ogre::Quaternion (Ogre::Radian (-time * 2 * Ogre::Math::PI), Ogre::Vector3::UNIT_Y); mNode->setOrientation (orientation); ensureGeometry (); } void PointStarfield::setObserverLatitude (Ogre::Degree value) { if (!Math::RealEqual ( mObserverLatitude.valueDegrees (), value.valueDegrees (), this->getObserverPositionRebuildDelta ().valueDegrees ())) { mObserverLatitude = value; invalidateGeometry (); } } void PointStarfield::setObserverLongitude (Ogre::Degree value) { if (!Math::RealEqual ( mObserverLongitude.valueDegrees (), value.valueDegrees (), this->getObserverPositionRebuildDelta ().valueDegrees ())) { mObserverLongitude = value; invalidateGeometry (); } } }