1 /*
2 This file is part of Caelum.
3 See http://www.ogre3d.org/wiki/index.php/Caelum
4 
5 Copyright (c) 2008 Caelum team. See Contributors.txt for details.
6 
7 Caelum is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Caelum is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public License
18 along with Caelum. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "CaelumPrecompiled.h"
22 #include "PointStarfield.h"
23 #include "CaelumExceptions.h"
24 #include "Astronomy.h"
25 #include "InternalUtilities.h"
26 
27 using namespace Ogre;
28 
29 namespace Caelum
30 {
31 	const Ogre::String PointStarfield::STARFIELD_MATERIAL_NAME = "Caelum/StarPoint";
32     const Ogre::Degree PointStarfield::DEFAULT_OBSERVER_POSITION_REBUILD_DELTA = Ogre::Degree(0.1);
33 
PointStarfield(Ogre::SceneManager * sceneMgr,Ogre::SceneNode * caelumRootNode,bool initWithCatalogue)34 	PointStarfield::PointStarfield (
35 			Ogre::SceneManager *sceneMgr,
36 			Ogre::SceneNode *caelumRootNode,
37 			bool initWithCatalogue)
38 	{
39 		mMag0PixelSize = 16;
40 		mMinPixelSize = 4;
41 		mMaxPixelSize = 6;
42 		mMagnitudeScale = Math::Pow(100, 0.2);
43 		mObserverLatitude = 45;
44 		mObserverLongitude = 0;
45         mObserverPositionRebuildDelta = DEFAULT_OBSERVER_POSITION_REBUILD_DELTA;
46 
47         String uniqueSuffix = "/" + InternalUtilities::pointerToString(this);
48 
49         // Load material.
50         mMaterial.reset(InternalUtilities::checkLoadMaterialClone(
51                     STARFIELD_MATERIAL_NAME,
52                     STARFIELD_MATERIAL_NAME + uniqueSuffix));
53 
54         mParams.setup(mMaterial->getTechnique(0)->getPass(0)->getVertexProgramParameters());
55 
56 		// We use a separate data source.
57 		Ogre::String objName = "Caelum/PointStarfield" + uniqueSuffix;
58         mManualObj.reset (sceneMgr->createManualObject (objName));
59         mManualObj->setDynamic(false);
60 		mManualObj->setRenderQueueGroup (CAELUM_RENDER_QUEUE_STARFIELD);
61 		sceneMgr->getRenderQueue()->getQueueGroup(CAELUM_RENDER_QUEUE_STARFIELD)->setShadowsEnabled (false);
62         mManualObj->setCastShadows(false);
63 
64 		mNode.reset (caelumRootNode->createChildSceneNode ());
65 		mNode->attachObject (mManualObj.getPointer ());
66 
67 		if (initWithCatalogue) {
68 			addBrightStarCatalogue ();
69 		}
70 	}
71 
~PointStarfield()72 	PointStarfield::~PointStarfield ()
73     {
74 	}
75 
notifyStarVectorChanged()76     void PointStarfield::notifyStarVectorChanged () {
77         invalidateGeometry ();
78     }
79 
clearAllStars()80     void PointStarfield::clearAllStars () {
81         mStars.clear();
82         notifyStarVectorChanged ();
83     }
84 
randReal()85 	Real randReal () {
86 		return rand() / static_cast<float>(RAND_MAX);
87 	}
88 
randReal(Real min,Real max)89 	Real randReal (Real min, Real max) {
90 		Real f = randReal ();
91 		return min * (1 - f) + max * f;
92 	}
93 
addRandomStars(int count)94 	void PointStarfield::addRandomStars (int count)
95 	{
96 		for (int i = 0; i < count; ++i) {
97 			// Generate a vector inside a sphere
98 			Ogre::Vector3 pos;
99 			do {
100 				pos.x = randReal(-1, 1);
101 				pos.y = randReal(-1, 1);
102 				pos.z = randReal(-1, 1);
103 			} while (pos.squaredLength () >= 1);
104 
105 			// Convert to rasc/decl angles.
106 			LongReal rasc, decl, dist;
107 			Astronomy::convertRectangularToSpherical(
108 					pos.x, pos.y, pos.z,
109 					rasc, decl, dist);
110 
111 			Star s;
112 			s.RightAscension = Ogre::Degree (rasc);
113 			s.Declination = Ogre::Degree (decl);
114 			// This distribution is wrong.
115 			s.Magnitude = 6 * pos.squaredLength () + 1.5;
116 			mStars.push_back(s);
117 		}
118 		notifyStarVectorChanged ();
119 	}
120 
addStar(const BrightStarCatalogueEntry & entry)121 	void PointStarfield::addStar (const BrightStarCatalogueEntry &entry) {
122 		Star s;
123 		s.RightAscension = Ogre::Degree(360 / 24.0f * (
124 				Math::Abs(entry.rasc_hour) +
125 				entry.rasc_min / 60.0f +
126 				entry.rasc_sec / 3600.0f));
127 		s.Declination = Ogre::Degree(Math::Sign(entry.decl_deg) * (
128 				Math::Abs(entry.decl_deg) +
129 				entry.decl_min / 60.0f +
130 				entry.decl_sec / 3600.0f));
131 		s.Magnitude = entry.magn;
132 		mStars.push_back(s);
133 
134         notifyStarVectorChanged ();
135 	}
136 
addBrightStarCatalogue(int count)137 	void PointStarfield::addBrightStarCatalogue (int count) {
138 		assert(count >= 0);
139 		if (count < BrightStarCatalogueSize) {
140 			// Only sort if we don't add everything.
141 			// It would be lovely if the catalogue was already sorted.
142 			std::vector<std::pair<Real, int> > vec;
143 			vec.reserve(BrightStarCatalogueSize);
144 			for (int i = 0; i < BrightStarCatalogueSize; ++i) {
145 				vec.push_back(std::make_pair(BrightStarCatalogue[i].magn, i));
146 			}
147 			sort(vec.begin(), vec.end());
148 			for (int i = 0; i < count; ++i) {
149 				addStar(BrightStarCatalogue[vec[i].second]);
150 			}
151 		} else {
152 			assert(count == BrightStarCatalogueSize);
153 			for (int i = 0; i < BrightStarCatalogueSize; ++i) {
154 				addStar(BrightStarCatalogue[i]);
155 			}
156 		}
157         notifyStarVectorChanged ();
158 	}
159 
invalidateGeometry()160 	void PointStarfield::invalidateGeometry () {
161 		mValidGeometry = false;
162 	}
163 
ensureGeometry()164 	void PointStarfield::ensureGeometry ()
165 	{
166 		if (mValidGeometry) {
167 			return;
168 		}
169 
170 		//Ogre::LogManager::getSingleton ().logMessage ("Caelum: Recomputing starfield geometry.");
171 
172         size_t starCount = mStars.size();
173 
174         mManualObj->clear();
175         mManualObj->estimateVertexCount(6 * starCount);
176         mManualObj->begin(mMaterial->getName (), Ogre::RenderOperation::OT_TRIANGLE_LIST);
177         for (uint i = 0; i < starCount; ++i)
178         {
179             const Star& star = mStars[i];
180 
181 			// Determine position at J2000
182 			LongReal azm, alt;
183 			Astronomy::convertEquatorialToHorizontal(
184 					Astronomy::J2000,
185 					mObserverLatitude.valueDegrees(),
186 					mObserverLongitude.valueDegrees(),
187 					star.RightAscension.valueDegrees(), star.Declination.valueDegrees(),
188 					azm, alt);
189 
190     		Ogre::Vector3 pos;
191 		    pos.z = -Math::Cos (Ogre::Degree(azm)) * Math::Cos (Ogre::Degree(alt));
192 		    pos.x =  Math::Sin (Ogre::Degree(azm)) * Math::Cos (Ogre::Degree(alt));
193 		    pos.y = -Math::Sin (Ogre::Degree(alt));
194 
195             //mManualObj->colour (Ogre::ColourValue::White);
196             mManualObj->position (pos);
197             mManualObj->textureCoord (+1, -1, star.Magnitude);
198             mManualObj->position (pos);
199             mManualObj->textureCoord (+1, +1, star.Magnitude);
200             mManualObj->position (pos);
201             mManualObj->textureCoord (-1, -1, star.Magnitude);
202 
203             mManualObj->position (pos);
204             mManualObj->textureCoord (-1, -1, star.Magnitude);
205             mManualObj->position (pos);
206             mManualObj->textureCoord (+1, +1, star.Magnitude);
207             mManualObj->position (pos);
208             mManualObj->textureCoord (-1, +1, star.Magnitude);
209         }
210         mManualObj->end();
211 
212         // Set finite bounds on the starfield to avoid parent AABB infection
213         AxisAlignedBox box(Ogre::AxisAlignedBox::EXTENT_FINITE);
214         mManualObj->setBoundingBox (box);
215 
216 		mValidGeometry = true;
217 	}
218 
setup(Ogre::GpuProgramParametersSharedPtr vpParams)219     void PointStarfield::Params::setup(Ogre::GpuProgramParametersSharedPtr vpParams)
220     {
221         this->vpParams = vpParams;
222         this->mag_scale.bind(vpParams, "mag_scale");
223         this->mag0_size.bind(vpParams, "mag0_size");
224         this->min_size.bind(vpParams, "min_size");
225         this->max_size.bind(vpParams, "max_size");
226         this->aspect_ratio.bind(vpParams, "aspect_ratio");
227     }
228 
notifyCameraChanged(Ogre::Camera * cam)229 	void PointStarfield::notifyCameraChanged (Ogre::Camera *cam) {
230 		CameraBoundElement::notifyCameraChanged (cam);
231 
232         // Shader params are changed for every camera.
233         Pass* pass = mMaterial->getBestTechnique ()->getPass (0);
234         GpuProgramParametersSharedPtr fpParams = pass->getFragmentProgramParameters ();
235         GpuProgramParametersSharedPtr vpParams = pass->getVertexProgramParameters ();
236 
237         int height = cam->getViewport ()-> getActualHeight ();
238         int width = cam->getViewport ()-> getActualWidth ();
239         Real pixFactor = 1.0f / width;
240         Real magScale = -Math::Log (mMagnitudeScale) / 2;
241         Real mag0Size = mMag0PixelSize * pixFactor;
242         Real minSize = mMinPixelSize * pixFactor;
243         Real maxSize = mMaxPixelSize * pixFactor;
244         Real aspectRatio = static_cast<Real>(width) / height;
245 
246         // These params are relative to the size of the screen.
247         mParams.mag_scale.set(mParams.vpParams, magScale);
248         mParams.mag0_size.set(mParams.vpParams, mag0Size);
249         mParams.min_size.set(mParams.vpParams, minSize);
250         mParams.max_size.set(mParams.vpParams, maxSize);
251         mParams.aspect_ratio.set(mParams.vpParams, aspectRatio);
252 	}
253 
setFarRadius(Ogre::Real radius)254 	void PointStarfield::setFarRadius (Ogre::Real radius) {
255         CameraBoundElement::setFarRadius(radius);
256 	    mNode->setScale (Ogre::Vector3::UNIT_SCALE * radius);
257 	}
258 
_update(const float time)259 	void PointStarfield::_update (const float time) {
260 		// This is probably wrong.
261 		Ogre::Quaternion orientation = Ogre::Quaternion::IDENTITY;
262 		orientation = orientation * Ogre::Quaternion (Ogre::Radian (-mObserverLatitude + Ogre::Degree (90)), Ogre::Vector3::UNIT_X);
263 		orientation = orientation * Ogre::Quaternion (Ogre::Radian (-time * 2 * Ogre::Math::PI), Ogre::Vector3::UNIT_Y);
264 		mNode->setOrientation (orientation);
265         ensureGeometry ();
266 	}
267 
setObserverLatitude(Ogre::Degree value)268 	void PointStarfield::setObserverLatitude (Ogre::Degree value)
269     {
270 		if (!Math::RealEqual (
271                 mObserverLatitude.valueDegrees (),
272                 value.valueDegrees (),
273                 this->getObserverPositionRebuildDelta ().valueDegrees ()))
274         {
275 			mObserverLatitude = value;
276 			invalidateGeometry ();
277 		}
278 	}
279 
setObserverLongitude(Ogre::Degree value)280 	void PointStarfield::setObserverLongitude (Ogre::Degree value)
281     {
282 		if (!Math::RealEqual (
283                 mObserverLongitude.valueDegrees (),
284                 value.valueDegrees (),
285                 this->getObserverPositionRebuildDelta ().valueDegrees ()))
286         {
287 			mObserverLongitude = value;
288 			invalidateGeometry ();
289 		}
290 	}
291 }
292