osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include <osgEarthUtil/SkyNode> 00020 #include <osgEarthUtil/StarData> 00021 00022 #include <osgEarth/ShaderComposition> 00023 #include <osgEarth/FindNode> 00024 #include <osgEarth/MapNode> 00025 00026 #include <osg/MatrixTransform> 00027 #include <osg/ShapeDrawable> 00028 #include <osg/BlendFunc> 00029 #include <osg/FrontFace> 00030 #include <osg/CullFace> 00031 #include <osg/Program> 00032 #include <osg/Point> 00033 #include <osg/Shape> 00034 #include <osg/Depth> 00035 #include <osg/Quat> 00036 00037 #include <sstream> 00038 #include <time.h> 00039 00040 #define LC "[SkyNode] " 00041 00042 using namespace osgEarth; 00043 using namespace osgEarth::Util; 00044 00045 //--------------------------------------------------------------------------- 00046 00047 #define BIN_STARS -10 00048 #define BIN_SUN -9 00049 #define BIN_ATMOSPHERE -8 00050 00051 //--------------------------------------------------------------------------- 00052 00053 namespace 00054 { 00055 // a cull callback that prevents objects from being included in the near/fear clip 00056 // plane calculates that OSG does. This is useful for including "distant objects" 00057 struct DoNotIncludeInNearFarComputationCallback : public osg::NodeCallback 00058 { 00059 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) 00060 { 00061 osgUtil::CullVisitor* cv = dynamic_cast< osgUtil::CullVisitor*>( nv ); 00062 00063 // Default value 00064 osg::CullSettings::ComputeNearFarMode oldMode; 00065 00066 if( cv ) 00067 { 00068 oldMode = cv->getComputeNearFarMode(); 00069 cv->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR ); 00070 } 00071 00072 traverse(node, nv); 00073 00074 if( cv ) 00075 { 00076 cv->setComputeNearFarMode(oldMode); 00077 } 00078 } 00079 }; 00080 00081 struct OverrideNearFarValuesCallback : public osg::Drawable::DrawCallback 00082 { 00083 OverrideNearFarValuesCallback(double radius) 00084 : _radius(radius) {} 00085 00086 virtual void drawImplementation(osg::RenderInfo& renderInfo, 00087 const osg::Drawable* drawable) const 00088 { 00089 osg::Camera* currentCamera = renderInfo.getCurrentCamera(); 00090 if (currentCamera) 00091 { 00092 // Get the current camera position. 00093 osg::Vec3 eye, center, up; 00094 renderInfo.getCurrentCamera()->getViewMatrixAsLookAt( eye, center, up); 00095 00096 // Get the max distance we need the far plane to be at, 00097 // which is the distance between the eye and the origin 00098 // plus the distant from the origin to the object (star sphere 00099 // radius, sun distance etc), and then some. 00100 double distance = eye.length() + _radius*2; 00101 00102 // Save old values. 00103 osg::ref_ptr<osg::RefMatrixd> oldProjectionMatrix = new osg::RefMatrix; 00104 oldProjectionMatrix->set( renderInfo.getState()->getProjectionMatrix()); 00105 00106 // Get the individual values 00107 double left, right, bottom, top, zNear, zFar; 00108 oldProjectionMatrix->getFrustum( left, right, bottom, top, zNear, zFar); 00109 00110 // Build a new projection matrix with a modified far plane 00111 osg::ref_ptr<osg::RefMatrixd> projectionMatrix = new osg::RefMatrix; 00112 //projectionMatrix->makeFrustum( left, right, bottom, top, zNear, distance); 00113 //OE_INFO << "zNear=" << zNear << ", zFar=" << zFar << std::endl; 00114 projectionMatrix->makeFrustum( left, right, bottom, top, zNear, distance ); 00115 renderInfo.getState()->applyProjectionMatrix( projectionMatrix.get()); 00116 00117 // Draw the drawable 00118 drawable->drawImplementation(renderInfo); 00119 00120 // Reset the far plane to the old value. 00121 renderInfo.getState()->applyProjectionMatrix( oldProjectionMatrix.get() ); 00122 } 00123 else 00124 { 00125 drawable->drawImplementation(renderInfo); 00126 } 00127 } 00128 00129 double _radius; 00130 }; 00131 00132 struct AddCallbackToDrawablesVisitor : public osg::NodeVisitor 00133 { 00134 AddCallbackToDrawablesVisitor(double radius) 00135 : osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), 00136 _radius(radius) {} 00137 00138 virtual void apply(osg::Geode& node) 00139 { 00140 for (unsigned int i = 0; i < node.getNumDrawables(); i++) 00141 { 00142 node.getDrawable(i)->setDrawCallback( new OverrideNearFarValuesCallback(_radius) ); 00143 00144 // Do not use display lists otherwise the callback will only 00145 // be called once on initial compile. 00146 node.getDrawable(i)->setUseDisplayList(false); 00147 } 00148 } 00149 00150 double _radius; 00151 }; 00152 00153 osg::Geometry* 00154 s_makeEllipsoidGeometry( const osg::EllipsoidModel* ellipsoid, double outerRadius ) 00155 { 00156 double hae = outerRadius - ellipsoid->getRadiusEquator(); 00157 00158 osg::Geometry* geom = new osg::Geometry(); 00159 00160 //geom->setUseVertexBufferObjects( true ); 00161 geom->setUseDisplayList( false ); 00162 00163 int latSegments = 100; 00164 int lonSegments = 2 * latSegments; 00165 00166 double segmentSize = 180.0/(double)latSegments; // degrees 00167 00168 osg::Vec3Array* verts = new osg::Vec3Array(); 00169 verts->reserve( latSegments * lonSegments ); 00170 00171 osg::DrawElementsUShort* el = new osg::DrawElementsUShort( GL_TRIANGLES ); 00172 el->reserve( latSegments * lonSegments * 6 ); 00173 00174 for( int y = 0; y <= latSegments; ++y ) 00175 { 00176 double lat = -90.0 + segmentSize * (double)y; 00177 for( int x = 0; x < lonSegments; ++x ) 00178 { 00179 double lon = -180.0 + segmentSize * (double)x; 00180 double gx, gy, gz; 00181 ellipsoid->convertLatLongHeightToXYZ( osg::DegreesToRadians(lat), osg::DegreesToRadians(lon), hae, gx, gy, gz ); 00182 verts->push_back( osg::Vec3(gx, gy, gz) ); 00183 00184 if ( y < latSegments ) 00185 { 00186 int x_plus_1 = x < lonSegments-1 ? x+1 : 0; 00187 int y_plus_1 = y+1; 00188 el->push_back( y*lonSegments + x ); 00189 el->push_back( y*lonSegments + x_plus_1 ); 00190 el->push_back( y_plus_1*lonSegments + x ); 00191 el->push_back( y*lonSegments + x_plus_1 ); 00192 el->push_back( y_plus_1*lonSegments + x_plus_1 ); 00193 el->push_back( y_plus_1*lonSegments + x ); 00194 } 00195 } 00196 } 00197 00198 geom->setVertexArray( verts ); 00199 geom->addPrimitiveSet( el ); 00200 00201 return geom; 00202 } 00203 00204 osg::Geometry* 00205 s_makeDiscGeometry( double radius ) 00206 { 00207 int segments = 48; 00208 float deltaAngle = 360.0/(float)segments; 00209 00210 osg::Geometry* geom = new osg::Geometry(); 00211 00212 //geom->setUseVertexBufferObjects( true ); 00213 geom->setUseDisplayList( false ); 00214 00215 osg::Vec3Array* verts = new osg::Vec3Array(); 00216 verts->reserve( 1 + segments ); 00217 geom->setVertexArray( verts ); 00218 00219 osg::DrawElementsUShort* el = new osg::DrawElementsUShort( GL_TRIANGLES ); 00220 el->reserve( 1 + 2*segments ); 00221 geom->addPrimitiveSet( el ); 00222 00223 verts->push_back( osg::Vec3(0,0,0) ); // center point 00224 00225 for( int i=0; i<segments; ++i ) 00226 { 00227 double angle = osg::DegreesToRadians( deltaAngle * (float)i ); 00228 double x = radius * cos( angle ); 00229 double y = radius * sin( angle ); 00230 verts->push_back( osg::Vec3(x, y, 0.0) ); 00231 00232 int i_plus_1 = i < segments-1? i+1 : 0; 00233 el->push_back( 0 ); 00234 el->push_back( 1 + i_plus_1 ); 00235 el->push_back( 1 + i ); 00236 } 00237 00238 return geom; 00239 } 00240 } 00241 00242 //--------------------------------------------------------------------------- 00243 00244 // Astronomical Math 00245 // http://www.stjarnhimlen.se/comp/ppcomp.html 00246 namespace 00247 { 00248 #define d2r(X) osg::DegreesToRadians(X) 00249 #define r2d(X) osg::RadiansToDegrees(X) 00250 #define nrad(X) { while( X > TWO_PI ) X -= TWO_PI; while( X < 0.0 ) X += TWO_PI; } 00251 #define nrad2(X) { while( X <= -osg::PI ) X += TWO_PI; while( X > osg::PI ) X -= TWO_PI; } 00252 00253 static const double TWO_PI = (2.0*osg::PI); 00254 static const double JD2000 = 2451545.0; 00255 00256 //double getTimeScale( int year, int month, int date, double hoursUT ) 00257 //{ 00258 // int a = 367*year - 7 * ( year + (month+9)/12 ) / 4 + 275*month/9 + date - 730530; 00259 // return (double)a + hoursUT/24.0; 00260 //} 00261 00262 double getJulianDate( int year, int month, int date ) 00263 { 00264 if ( month <= 2 ) 00265 { 00266 month += 12; 00267 year -= 1; 00268 } 00269 00270 int A = int(year/100); 00271 int B = 2-A+(A/4); 00272 int C = int(365.25*(year+4716)); 00273 int D = int(30.6001*(month+1)); 00274 return B + C + D + date - 1524.5; 00275 } 00276 00277 struct Sun 00278 { 00279 Sun() { } 00280 00281 // https://www.cfa.harvard.edu/~wsoon/JuanRamirez09-d/Chang09-OptimalTiltAngleforSolarCollector.pdf 00282 osg::Vec3d getPosition(int year, int month, int date, double hoursUTC ) const 00283 { 00284 double JD = getJulianDate(year, month, date); 00285 double JD1 = (JD - JD2000); // julian time since JD2000 epoch 00286 double JC = JD1/36525.0; // julian century 00287 00288 double mu = 282.937348 + 0.00004707624*JD1 + 0.0004569*(JC*JC); 00289 00290 double epsilon = 280.466457 + 0.985647358*JD1 + 0.000304*(JC*JC); 00291 00292 // orbit eccentricity: 00293 double E = 0.01670862 - 0.00004204 * JC; 00294 00295 // mean anomaly of the perihelion 00296 double M = epsilon - mu; 00297 00298 // perihelion anomaly: 00299 double v = 00300 M + 00301 360.0*E*sin(d2r(M))/osg::PI + 00302 900.0*(E*E)*sin(d2r(2*M))/4*osg::PI - 00303 180.0*(E*E*E)*sin(d2r(M))/4.0*osg::PI; 00304 00305 // longitude of the sun in ecliptic coordinates: 00306 double sun_lon = d2r(v - 360.0 + mu); // lambda 00307 nrad2(sun_lon); 00308 00309 // angle between the ecliptic plane and the equatorial plane 00310 double zeta = d2r(23.4392); // zeta 00311 00312 // latitude of the sun on the ecliptic plane: 00313 double omega = d2r(0.0); 00314 00315 // latitude of the sun with respect to the equatorial plane (solar declination): 00316 double sun_lat = asin( sin(sun_lon)*sin(zeta) ); 00317 nrad2(sun_lat); 00318 00319 // finally, adjust for the time of day (rotation of the earth) 00320 double time_r = hoursUTC/24.0; // 0..1 00321 nrad(sun_lon); // clamp to 0..TWO_PI 00322 double sun_r = sun_lon/TWO_PI; // convert to 0..1 00323 00324 // rotational difference between UTC and current time 00325 double diff_r = sun_r - time_r; 00326 double diff_lon = TWO_PI * diff_r; 00327 00328 // apparent sun longitude. 00329 double app_sun_lon = sun_lon - diff_lon + osg::PI; 00330 nrad2(app_sun_lon); 00331 00332 #if 0 00333 OE_INFO 00334 << "sun lat = " << r2d(sun_lat) 00335 << ", sun lon = " << r2d(sun_lon) 00336 << ", time delta_lon = " << r2d(diff_lon) 00337 << ", app sun lon = " << r2d(app_sun_lon) 00338 << std::endl; 00339 #endif 00340 00341 return osg::Vec3d( 00342 cos(sun_lat) * cos(-app_sun_lon), 00343 cos(sun_lat) * sin(-app_sun_lon), 00344 sin(sun_lat) ); 00345 } 00346 }; 00347 } 00348 00349 //--------------------------------------------------------------------------- 00350 00351 namespace 00352 { 00353 // Atmospheric Scattering and Sun Shaders 00354 // Adapted from code that is 00355 // Copyright (c) 2004 Sean O'Neil 00356 00357 static char s_atmosphereVertexSource[] = 00358 "#version 110 \n" 00359 00360 "uniform mat4 osg_ViewMatrixInverse; // camera position \n" 00361 "uniform vec3 atmos_v3LightPos; // The direction vector to the light source \n" 00362 "uniform vec3 atmos_v3InvWavelength; // 1 / pow(wavelength,4) for the rgb channels \n" 00363 "uniform float atmos_fOuterRadius; // Outer atmosphere radius \n" 00364 "uniform float atmos_fOuterRadius2; // fOuterRadius^2 \n" 00365 "uniform float atmos_fInnerRadius; // Inner planetary radius \n" 00366 "uniform float atmos_fInnerRadius2; // fInnerRadius^2 \n" 00367 "uniform float atmos_fKrESun; // Kr * ESun \n" 00368 "uniform float atmos_fKmESun; // Km * ESun \n" 00369 "uniform float atmos_fKr4PI; // Kr * 4 * PI \n" 00370 "uniform float atmos_fKm4PI; // Km * 4 * PI \n" 00371 "uniform float atmos_fScale; // 1 / (fOuterRadius - fInnerRadius) \n" 00372 "uniform float atmos_fScaleDepth; // The scale depth \n" 00373 "uniform float atmos_fScaleOverScaleDepth; // fScale / fScaleDepth \n" 00374 "uniform int atmos_nSamples; \n" 00375 "uniform float atmos_fSamples; \n" 00376 00377 "varying vec3 atmos_v3Direction; \n" 00378 "varying vec3 atmos_mieColor; \n" 00379 "varying vec3 atmos_rayleighColor; \n" 00380 00381 "vec3 vVec; \n" 00382 "float atmos_fCameraHeight; // The camera's current height \n" 00383 "float atmos_fCameraHeight2; // fCameraHeight^2 \n" 00384 00385 "float atmos_scale(float fCos) \n" 00386 "{ \n" 00387 " float x = 1.0 - fCos; \n" 00388 " return atmos_fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25)))); \n" 00389 "} \n" 00390 00391 "void SkyFromSpace(void) \n" 00392 "{ \n" 00393 " // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere) \n" 00394 " vec3 v3Pos = gl_Vertex.xyz; \n" 00395 " vec3 v3Ray = v3Pos - vVec; \n" 00396 " float fFar = length(v3Ray); \n" 00397 " v3Ray /= fFar; \n" 00398 00399 " // Calculate the closest intersection of the ray with the outer atmosphere \n" 00400 " // (which is the near point of the ray passing through the atmosphere) \n" 00401 " float B = 2.0 * dot(vVec, v3Ray); \n" 00402 " float C = atmos_fCameraHeight2 - atmos_fOuterRadius2; \n" 00403 " float fDet = max(0.0, B*B - 4.0 * C); \n" 00404 " float fNear = 0.5 * (-B - sqrt(fDet)); \n" 00405 00406 " // Calculate the ray's starting position, then calculate its atmos_ing offset \n" 00407 " vec3 v3Start = vVec + v3Ray * fNear; \n" 00408 " fFar -= fNear; \n" 00409 " float fStartAngle = dot(v3Ray, v3Start) / atmos_fOuterRadius; \n" 00410 " float fStartDepth = exp(-1.0 / atmos_fScaleDepth); \n" 00411 " float fStartOffset = fStartDepth*atmos_scale(fStartAngle); \n" 00412 00413 " // Initialize the atmos_ing loop variables \n" 00414 " float fSampleLength = fFar / atmos_fSamples; \n" 00415 " float fScaledLength = fSampleLength * atmos_fScale; \n" 00416 " vec3 v3SampleRay = v3Ray * fSampleLength; \n" 00417 " vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; \n" 00418 00419 " // Now loop through the sample rays \n" 00420 " vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); \n" 00421 " vec3 v3Attenuate; \n" 00422 " for(int i=0; i<atmos_nSamples; i++) \n" 00423 " { \n" 00424 " float fHeight = length(v3SamplePoint); \n" 00425 " float fDepth = exp(atmos_fScaleOverScaleDepth * (atmos_fInnerRadius - fHeight)); \n" 00426 " float fLightAngle = dot(atmos_v3LightPos, v3SamplePoint) / fHeight; \n" 00427 " float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; \n" 00428 " float fscatter = (fStartOffset + fDepth*(atmos_scale(fLightAngle) - atmos_scale(fCameraAngle))); \n" 00429 " v3Attenuate = exp(-fscatter * (atmos_v3InvWavelength * atmos_fKr4PI + atmos_fKm4PI)); \n" 00430 " v3FrontColor += v3Attenuate * (fDepth * fScaledLength); \n" 00431 " v3SamplePoint += v3SampleRay; \n" 00432 " } \n" 00433 00434 " // Finally, scale the Mie and Rayleigh colors and set up the varying \n" 00435 " // variables for the pixel shader \n" 00436 " atmos_mieColor = v3FrontColor * atmos_fKmESun; \n" 00437 " atmos_rayleighColor = v3FrontColor * (atmos_v3InvWavelength * atmos_fKrESun); \n" 00438 " atmos_v3Direction = vVec - v3Pos; \n" 00439 "} \n" 00440 00441 "void SkyFromAtmosphere(void) \n" 00442 "{ \n" 00443 " // Get the ray from the camera to the vertex, and its length (which is the far \n" 00444 " // point of the ray passing through the atmosphere) \n" 00445 " vec3 v3Pos = gl_Vertex.xyz; \n" 00446 " vec3 v3Ray = v3Pos - vVec; \n" 00447 " float fFar = length(v3Ray); \n" 00448 " v3Ray /= fFar; \n" 00449 00450 " // Calculate the ray's starting position, then calculate its atmos_ing offset \n" 00451 " vec3 v3Start = vVec; \n" 00452 " float fHeight = length(v3Start); \n" 00453 " float fDepth = exp(atmos_fScaleOverScaleDepth * (atmos_fInnerRadius - atmos_fCameraHeight)); \n" 00454 " float fStartAngle = dot(v3Ray, v3Start) / fHeight; \n" 00455 " float fStartOffset = fDepth*atmos_scale(fStartAngle); \n" 00456 00457 " // Initialize the atmos_ing loop variables \n" 00458 " float fSampleLength = fFar / atmos_fSamples; \n" 00459 " float fScaledLength = fSampleLength * atmos_fScale; \n" 00460 " vec3 v3SampleRay = v3Ray * fSampleLength; \n" 00461 " vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5; \n" 00462 00463 " // Now loop through the sample rays \n" 00464 " vec3 v3FrontColor = vec3(0.0, 0.0, 0.0); \n" 00465 " vec3 v3Attenuate; \n" 00466 " for(int i=0; i<atmos_nSamples; i++) \n" 00467 " { \n" 00468 " float fHeight = length(v3SamplePoint); \n" 00469 " float fDepth = exp(atmos_fScaleOverScaleDepth * (atmos_fInnerRadius - fHeight)); \n" 00470 " float fLightAngle = dot(atmos_v3LightPos, v3SamplePoint) / fHeight; \n" 00471 " float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; \n" 00472 " float fscatter = (fStartOffset + fDepth*(atmos_scale(fLightAngle) - atmos_scale(fCameraAngle))); \n" 00473 " v3Attenuate = exp(-fscatter * (atmos_v3InvWavelength * atmos_fKr4PI + atmos_fKm4PI)); \n" 00474 " v3FrontColor += v3Attenuate * (fDepth * fScaledLength); \n" 00475 " v3SamplePoint += v3SampleRay; \n" 00476 " } \n" 00477 00478 " // Finally, scale the Mie and Rayleigh colors and set up the varying \n" 00479 " // variables for the pixel shader \n" 00480 " atmos_mieColor = v3FrontColor * atmos_fKmESun; \n" 00481 " atmos_rayleighColor = v3FrontColor * (atmos_v3InvWavelength * atmos_fKrESun); \n" 00482 " atmos_v3Direction = vVec - v3Pos; \n" 00483 "} \n" 00484 00485 "void main(void) \n" 00486 "{ \n" 00487 " // Get camera position and height \n" 00488 " vVec = osg_ViewMatrixInverse[3].xyz; \n" 00489 " atmos_fCameraHeight = length(vVec); \n" 00490 " atmos_fCameraHeight2 = atmos_fCameraHeight*atmos_fCameraHeight; \n" 00491 " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n" 00492 " if(atmos_fCameraHeight >= atmos_fOuterRadius) { \n" 00493 " SkyFromSpace(); \n" 00494 " } \n" 00495 " else { \n" 00496 " SkyFromAtmosphere(); \n" 00497 " } \n" 00498 "} \n"; 00499 00500 static char s_atmosphereFragmentSource[] = 00501 "#version 110 \n" 00502 00503 "float fastpow( in float x, in float y ) \n" 00504 "{ \n" 00505 " return x/(x+y-y*x); \n" 00506 "} \n" 00507 00508 "uniform vec3 atmos_v3LightPos; \n" 00509 "uniform float atmos_g; \n" 00510 "uniform float atmos_g2; \n" 00511 "uniform float atmos_fWeather; \n" 00512 00513 "varying vec3 atmos_v3Direction; \n" 00514 "varying vec3 atmos_mieColor; \n" 00515 "varying vec3 atmos_rayleighColor; \n" 00516 00517 "const float fExposure = 4.0; \n" 00518 00519 "void main(void) \n" 00520 "{ \n" 00521 " float fCos = dot(atmos_v3LightPos, atmos_v3Direction) / length(atmos_v3Direction); \n" 00522 " float fRayleighPhase = 1.0; \n" // 0.75 * (1.0 + fCos*fCos); \n" 00523 " float fMiePhase = 1.5 * ((1.0 - atmos_g2) / (2.0 + atmos_g2)) * (1.0 + fCos*fCos) / fastpow(1.0 + atmos_g2 - 2.0*atmos_g*fCos, 1.5); \n" 00524 " vec3 f4Color = fRayleighPhase * atmos_rayleighColor + fMiePhase * atmos_mieColor; \n" 00525 " vec3 color = 1.0 - exp(f4Color * -fExposure); \n" 00526 " gl_FragColor.rgb = color.rgb*atmos_fWeather; \n" 00527 " gl_FragColor.a = (color.r+color.g+color.b) * 2.0; \n" 00528 "} \n"; 00529 00530 static char s_sunVertexSource[] = 00531 "#version 110 \n" 00532 "varying vec3 atmos_v3Direction; \n" 00533 00534 "void main() \n" 00535 "{ \n" 00536 " vec3 v3Pos = gl_Vertex.xyz; \n" 00537 " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n" 00538 " atmos_v3Direction = vec3(0.0,0.0,1.0) - v3Pos; \n" 00539 " atmos_v3Direction = atmos_v3Direction/length(atmos_v3Direction); \n" 00540 "} \n"; 00541 00542 static char s_sunFragmentSource[] = 00543 "#version 110 \n" 00544 00545 "float fastpow( in float x, in float y ) \n" 00546 "{ \n" 00547 " return x/(x+y-y*x); \n" 00548 "} \n" 00549 00550 "uniform float sunAlpha; \n" 00551 "varying vec3 atmos_v3Direction; \n" 00552 00553 "void main( void ) \n" 00554 "{ \n" 00555 " float fCos = -atmos_v3Direction[2]; \n" 00556 " float fMiePhase = 0.050387596899224826 * (1.0 + fCos*fCos) / fastpow(1.9024999999999999 - -1.8999999999999999*fCos, 1.5); \n" 00557 " gl_FragColor.rgb = fMiePhase*vec3(.3,.3,.2); \n" 00558 " gl_FragColor.a = sunAlpha*gl_FragColor.r; \n" 00559 "} \n"; 00560 } 00561 00562 //--------------------------------------------------------------------------- 00563 00564 namespace 00565 { 00566 static const char s_starVertexSource[] = 00567 "void main() \n" 00568 "{ \n" 00569 " gl_FrontColor = vec4(1,1,1,1); \n" 00570 " gl_PointSize = gl_Color.r + 1.0f; \n" 00571 " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n" 00572 "} \n"; 00573 00574 static const char s_starFragmentSource[] = 00575 "void main( void ) \n" 00576 "{ \n" 00577 " gl_FragColor = gl_Color; \n" 00578 "} \n"; 00579 } 00580 00581 //--------------------------------------------------------------------------- 00582 00583 SkyNode::SkyNode( Map* map, const std::string& starFile ) 00584 { 00585 // intialize the default settings: 00586 _defaultPerViewData._lightPos.set( osg::Vec3f(0.0f, 1.0f, 0.0f) ); 00587 _defaultPerViewData._light = new osg::Light( 0 ); 00588 _defaultPerViewData._light->setPosition( osg::Vec4( _defaultPerViewData._lightPos, 0 ) ); 00589 _defaultPerViewData._light->setAmbient( osg::Vec4(0.4f, 0.4f, 0.4f ,1.0) ); 00590 _defaultPerViewData._light->setDiffuse( osg::Vec4(1,1,1,1) ); 00591 _defaultPerViewData._light->setSpecular( osg::Vec4(0,0,0,1) ); 00592 _defaultPerViewData._starsVisible = true; 00593 00594 // set up the astronomical parameters: 00595 _ellipsoidModel = map->getProfile()->getSRS()->getGeographicSRS()->getEllipsoid(); 00596 _innerRadius = _ellipsoidModel->getRadiusPolar(); 00597 _outerRadius = _innerRadius * 1.025f; 00598 _sunDistance = _innerRadius * 12000.0f; 00599 00600 // make the ephemeris (note: order is important here) 00601 makeAtmosphere( _ellipsoidModel.get() ); 00602 makeSun(); 00603 makeStars(starFile); 00604 } 00605 00606 osg::BoundingSphere 00607 SkyNode::computeBound() const 00608 { 00609 return osg::BoundingSphere(); 00610 } 00611 00612 void 00613 SkyNode::traverse( osg::NodeVisitor& nv ) 00614 { 00615 osg::CullSettings::ComputeNearFarMode saveMode; 00616 00617 osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv ); 00618 if ( cv ) 00619 { 00620 saveMode = cv->getComputeNearFarMode(); 00621 cv->setComputeNearFarMode( osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR ); 00622 00623 osg::View* view = cv->getCurrentCamera()->getView(); 00624 PerViewDataMap::iterator i = _perViewData.find( view ); 00625 if ( i != _perViewData.end() ) 00626 { 00627 i->second._cullContainer->accept( nv ); 00628 } 00629 } 00630 00631 osg::Group::traverse( nv ); 00632 00633 if ( cv ) 00634 { 00635 cv->setComputeNearFarMode( saveMode ); 00636 } 00637 } 00638 00639 void 00640 SkyNode::attach( osg::View* view, int lightNum ) 00641 { 00642 if ( !view ) return; 00643 00644 // creates the new per-view if it does not already exist 00645 PerViewData& data = _perViewData[view]; 00646 00647 data._light = osg::clone( _defaultPerViewData._light.get() ); 00648 data._light->setLightNum( lightNum ); 00649 data._light->setAmbient( _defaultPerViewData._light->getAmbient() ); 00650 data._lightPos = _defaultPerViewData._lightPos; 00651 00652 // the cull callback has to be on a parent group-- won't work on the xforms themselves. 00653 data._cullContainer = new osg::Group(); 00654 00655 data._sunXform = new osg::MatrixTransform(); 00656 data._sunMatrix = osg::Matrixd::translate( 00657 _sunDistance * data._lightPos.x(), 00658 _sunDistance * data._lightPos.y(), 00659 _sunDistance * data._lightPos.z() ); 00660 data._sunXform->setMatrix( data._sunMatrix ); 00661 data._sunXform->addChild( _sun.get() ); 00662 data._cullContainer->addChild( data._sunXform.get() ); 00663 00664 data._starsXform = new osg::MatrixTransform(); 00665 data._starsMatrix = _defaultPerViewData._starsMatrix; 00666 data._starsXform->setMatrix( _defaultPerViewData._starsMatrix ); 00667 data._starsXform->addChild( _stars.get() ); 00668 data._cullContainer->addChild( data._starsXform.get() ); 00669 00670 data._starsVisible = true; 00671 00672 data._cullContainer->addChild( _atmosphere.get() ); 00673 data._lightPosUniform = osg::clone( _defaultPerViewData._lightPosUniform.get() ); 00674 00675 view->setLightingMode( osg::View::SKY_LIGHT ); 00676 view->setLight( data._light.get() ); 00677 view->getCamera()->setClearColor( osg::Vec4(0,0,0,1) ); 00678 } 00679 00680 void 00681 SkyNode::setAmbientBrightness( float value, osg::View* view ) 00682 { 00683 if ( !view ) 00684 { 00685 setAmbientBrightness( _defaultPerViewData, value ); 00686 00687 for( PerViewDataMap::iterator i = _perViewData.begin(); i != _perViewData.end(); ++i ) 00688 setAmbientBrightness( i->second, value ); 00689 } 00690 else if ( _perViewData.find(view) != _perViewData.end() ) 00691 { 00692 setAmbientBrightness( _perViewData[view], value ); 00693 } 00694 } 00695 00696 float 00697 SkyNode::getAmbientBrightness( osg::View* view ) const 00698 { 00699 if ( view ) 00700 { 00701 PerViewDataMap::const_iterator i = _perViewData.find(view); 00702 if ( i != _perViewData.end() ) 00703 return i->second._light->getAmbient().r(); 00704 } 00705 return _defaultPerViewData._light->getAmbient().r(); 00706 } 00707 00708 void 00709 SkyNode::setAmbientBrightness( PerViewData& data, float value ) 00710 { 00711 value = osg::clampBetween( value, 0.0f, 1.0f ); 00712 data._light->setAmbient( osg::Vec4f(value, value, value, 1.0f) ); 00713 } 00714 00715 void 00716 SkyNode::setSunPosition( const osg::Vec3& pos, osg::View* view ) 00717 { 00718 if ( !view ) 00719 { 00720 setSunPosition( _defaultPerViewData, pos ); 00721 for( PerViewDataMap::iterator i = _perViewData.begin(); i != _perViewData.end(); ++i ) 00722 setSunPosition( i->second, pos ); 00723 } 00724 else if ( _perViewData.find(view) != _perViewData.end() ) 00725 { 00726 setSunPosition( _perViewData[view], pos ); 00727 } 00728 } 00729 00730 void 00731 SkyNode::setSunPosition( PerViewData& data, const osg::Vec3& pos ) 00732 { 00733 data._lightPos = pos; 00734 00735 if ( data._light.valid() ) 00736 data._light->setPosition( osg::Vec4( data._lightPos, 0 ) ); 00737 00738 if ( data._lightPosUniform.valid() ) 00739 data._lightPosUniform->set( data._lightPos / data._lightPos.length() ); 00740 00741 if ( data._sunXform.valid() ) 00742 { 00743 data._sunXform->setMatrix( osg::Matrix::translate( 00744 _sunDistance * data._lightPos.x(), 00745 _sunDistance * data._lightPos.y(), 00746 _sunDistance * data._lightPos.z() ) ); 00747 } 00748 } 00749 00750 void 00751 SkyNode::setSunPosition( double lat_degrees, double long_degrees, osg::View* view ) 00752 { 00753 if (_ellipsoidModel.valid()) 00754 { 00755 double x, y, z; 00756 _ellipsoidModel->convertLatLongHeightToXYZ( 00757 osg::RadiansToDegrees(lat_degrees), 00758 osg::RadiansToDegrees(long_degrees), 00759 0, 00760 x, y, z); 00761 osg::Vec3d up = _ellipsoidModel->computeLocalUpVector(x, y, z); 00762 setSunPosition( up, view ); 00763 } 00764 } 00765 00766 void 00767 SkyNode::setDateTime( int year, int month, int date, double hoursUTC, osg::View* view ) 00768 { 00769 if ( _ellipsoidModel.valid() ) 00770 { 00771 // position the sun: 00772 Sun sun; 00773 osg::Vec3d pos = sun.getPosition( year, month, date, hoursUTC ); 00774 pos.normalize(); 00775 setSunPosition( pos, view ); 00776 00777 // position the stars: 00778 double time_r = hoursUTC/24.0; // 0..1 00779 double rot_z = -osg::PI + TWO_PI*time_r; 00780 00781 osg::Matrixd starsMatrix = osg::Matrixd::rotate( -rot_z, 0, 0, 1 ); 00782 if ( !view ) 00783 { 00784 _defaultPerViewData._starsMatrix = starsMatrix; 00785 for( PerViewDataMap::iterator i = _perViewData.begin(); i != _perViewData.end(); ++i ) 00786 { 00787 i->second._starsMatrix = starsMatrix; 00788 i->second._starsXform->setMatrix( starsMatrix ); 00789 } 00790 } 00791 else if ( _perViewData.find(view) != _perViewData.end() ) 00792 { 00793 PerViewData& data = _perViewData[view]; 00794 data._starsMatrix = starsMatrix; 00795 data._starsXform->setMatrix( starsMatrix ); 00796 } 00797 } 00798 } 00799 00800 void 00801 SkyNode::setStarsVisible( bool value, osg::View* view ) 00802 { 00803 if ( !view ) 00804 { 00805 _defaultPerViewData._starsVisible = value; 00806 for( PerViewDataMap::iterator i = _perViewData.begin(); i != _perViewData.end(); ++i ) 00807 { 00808 i->second._starsVisible = value; 00809 i->second._starsXform->setNodeMask( value ? ~0 : 0 ); 00810 } 00811 } 00812 else if ( _perViewData.find(view) != _perViewData.end() ) 00813 { 00814 _perViewData[view]._starsVisible = value; 00815 _perViewData[view]._starsXform->setNodeMask( value ? ~0 : 0 ); 00816 } 00817 } 00818 00819 bool 00820 SkyNode::getStarsVisible( osg::View* view ) const 00821 { 00822 PerViewDataMap::const_iterator i = _perViewData.find(view); 00823 00824 if ( !view || i == _perViewData.end() ) 00825 { 00826 return _defaultPerViewData._starsVisible; 00827 } 00828 else 00829 { 00830 return i->second._starsVisible; 00831 } 00832 } 00833 00834 void 00835 SkyNode::makeAtmosphere( const osg::EllipsoidModel* em ) 00836 { 00837 // create some skeleton geometry to shade: 00838 osg::Geometry* drawable = s_makeEllipsoidGeometry( em, _outerRadius ); 00839 00840 osg::Geode* geode = new osg::Geode(); 00841 geode->addDrawable( drawable ); 00842 00843 osg::StateSet* set = geode->getOrCreateStateSet(); 00844 00845 // configure the state set: 00846 set->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); 00847 set->setMode( GL_CULL_FACE, osg::StateAttribute::ON ); 00848 set->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); 00849 //set->setBinNumber( 65 ); // todo, what? 00850 set->setBinNumber( BIN_ATMOSPHERE ); 00851 set->setAttributeAndModes( new osg::Depth( osg::Depth::LESS, 0, 1, false ) ); // no depth write 00852 set->setAttributeAndModes( new osg::BlendFunc( GL_ONE, GL_ONE ), osg::StateAttribute::ON ); 00853 set->setAttributeAndModes( new osg::FrontFace( osg::FrontFace::CLOCKWISE ), osg::StateAttribute::ON ); 00854 00855 // next, create and add the shaders: 00856 osg::Program* program = new osg::Program(); 00857 osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, s_atmosphereVertexSource ); 00858 program->addShader( vs ); 00859 osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, s_atmosphereFragmentSource ); 00860 program->addShader( fs ); 00861 set->setAttributeAndModes( program, osg::StateAttribute::ON ); 00862 00863 // apply the uniforms: 00864 float r_wl = ::powf( .65f, 4.0f ); 00865 float g_wl = ::powf( .57f, 4.0f ); 00866 float b_wl = ::powf( .475f, 4.0f ); 00867 osg::Vec3 RGB_wl( 1.0f/r_wl, 1.0f/g_wl, 1.0f/b_wl ); 00868 float Kr = 0.0025f; 00869 float Kr4PI = Kr * 4.0f * osg::PI; 00870 float Km = 0.0015f; 00871 float Km4PI = Km * 4.0f * osg::PI; 00872 float ESun = 15.0f; 00873 float MPhase = -.095f; 00874 float RayleighScaleDepth = 0.25f; 00875 int Samples = 2; 00876 float Weather = 1.0f; 00877 00878 float Scale = 1.0f / (_outerRadius - _innerRadius); 00879 00880 _defaultPerViewData._lightPosUniform = set->getOrCreateUniform( "atmos_v3LightPos", osg::Uniform::FLOAT_VEC3 ); 00881 _defaultPerViewData._lightPosUniform->set( _defaultPerViewData._lightPos / _defaultPerViewData._lightPos.length() ); 00882 00883 set->getOrCreateUniform( "atmos_v3InvWavelength", osg::Uniform::FLOAT_VEC3 )->set( RGB_wl ); 00884 set->getOrCreateUniform( "atmos_fInnerRadius", osg::Uniform::FLOAT )->set( _innerRadius ); 00885 set->getOrCreateUniform( "atmos_fInnerRadius2", osg::Uniform::FLOAT )->set( _innerRadius * _innerRadius ); 00886 set->getOrCreateUniform( "atmos_fOuterRadius", osg::Uniform::FLOAT )->set( _outerRadius ); 00887 set->getOrCreateUniform( "atmos_fOuterRadius2", osg::Uniform::FLOAT )->set( _outerRadius * _outerRadius ); 00888 set->getOrCreateUniform( "atmos_fKrESun", osg::Uniform::FLOAT )->set( Kr * ESun ); 00889 set->getOrCreateUniform( "atmos_fKmESun", osg::Uniform::FLOAT )->set( Km * ESun ); 00890 set->getOrCreateUniform( "atmos_fKr4PI", osg::Uniform::FLOAT )->set( Kr4PI ); 00891 set->getOrCreateUniform( "atmos_fKm4PI", osg::Uniform::FLOAT )->set( Km4PI ); 00892 set->getOrCreateUniform( "atmos_fScale", osg::Uniform::FLOAT )->set( Scale ); 00893 set->getOrCreateUniform( "atmos_fScaleDepth", osg::Uniform::FLOAT )->set( RayleighScaleDepth ); 00894 set->getOrCreateUniform( "atmos_fScaleOverScaleDepth", osg::Uniform::FLOAT )->set( Scale / RayleighScaleDepth ); 00895 set->getOrCreateUniform( "atmos_g", osg::Uniform::FLOAT )->set( MPhase ); 00896 set->getOrCreateUniform( "atmos_g2", osg::Uniform::FLOAT )->set( MPhase * MPhase ); 00897 set->getOrCreateUniform( "atmos_nSamples", osg::Uniform::INT )->set( Samples ); 00898 set->getOrCreateUniform( "atmos_fSamples", osg::Uniform::FLOAT )->set( (float)Samples ); 00899 set->getOrCreateUniform( "atmos_fWeather", osg::Uniform::FLOAT )->set( Weather ); 00900 00901 //geode->setCullCallback( new DoNotIncludeInNearFarComputationCallback() ); 00902 AddCallbackToDrawablesVisitor visitor( _innerRadius ); 00903 geode->accept( visitor ); 00904 00905 _atmosphere = geode; 00906 } 00907 00908 void 00909 SkyNode::makeSun() 00910 { 00911 osg::Billboard* sun = new osg::Billboard(); 00912 sun->setMode( osg::Billboard::POINT_ROT_EYE ); 00913 sun->setNormal( osg::Vec3(0, 0, 1) ); 00914 00915 float sunRadius = _innerRadius * 100.0f; 00916 00917 sun->addDrawable( s_makeDiscGeometry( sunRadius*80.0f ) ); 00918 00919 osg::StateSet* set = sun->getOrCreateStateSet(); 00920 00921 set->getOrCreateUniform( "sunAlpha", osg::Uniform::FLOAT )->set( 1.0f ); 00922 00923 // configure the stateset 00924 set->setMode( GL_LIGHTING, osg::StateAttribute::OFF ); 00925 set->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); 00926 set->setRenderBinDetails( BIN_SUN, "RenderBin" ); 00927 set->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), osg::StateAttribute::ON ); 00928 set->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); 00929 00930 // create shaders 00931 osg::Program* program = new osg::Program(); 00932 osg::Shader* vs = new osg::Shader( osg::Shader::VERTEX, s_sunVertexSource ); 00933 program->addShader( vs ); 00934 osg::Shader* fs = new osg::Shader( osg::Shader::FRAGMENT, s_sunFragmentSource ); 00935 program->addShader( fs ); 00936 set->setAttributeAndModes( program, osg::StateAttribute::ON ); 00937 00938 // make the sun's transform: 00939 // todo: move this? 00940 _defaultPerViewData._sunXform = new osg::MatrixTransform(); 00941 _defaultPerViewData._sunXform->setMatrix( osg::Matrix::translate( 00942 _sunDistance * _defaultPerViewData._lightPos.x(), 00943 _sunDistance * _defaultPerViewData._lightPos.y(), 00944 _sunDistance * _defaultPerViewData._lightPos.z() ) ); 00945 _defaultPerViewData._sunXform->addChild( sun ); 00946 00947 AddCallbackToDrawablesVisitor visitor( _sunDistance ); 00948 sun->accept( visitor ); 00949 00950 _sun = sun; 00951 } 00952 00953 SkyNode::StarData::StarData(std::stringstream &ss) 00954 { 00955 std::getline( ss, name, ',' ); 00956 std::string buff; 00957 std::getline( ss, buff, ',' ); 00958 std::stringstream(buff) >> right_ascension; 00959 std::getline( ss, buff, ',' ); 00960 std::stringstream(buff) >> declination; 00961 std::getline( ss, buff, '\n' ); 00962 std::stringstream(buff) >> magnitude; 00963 } 00964 00965 void 00966 SkyNode::makeStars(const std::string& starFile) 00967 { 00968 _starRadius = 20000.0 * (_sunDistance > 0.0 ? _sunDistance : _outerRadius); 00969 00970 std::vector<StarData> stars; 00971 00972 if( starFile.empty() || parseStarFile(starFile, stars) == false ) 00973 { 00974 if( !starFile.empty() ) 00975 OE_WARN << "Warning: Unable to use star field defined in file \"" << starFile << "\", using default star data." << std::endl; 00976 00977 getDefaultStars(stars); 00978 } 00979 00980 osg::Node* starNode = buildStarGeometry(stars); 00981 00982 AddCallbackToDrawablesVisitor visitor(_starRadius); 00983 starNode->accept(visitor); 00984 00985 _stars = starNode; 00986 } 00987 00988 osg::Geode* 00989 SkyNode::buildStarGeometry(const std::vector<StarData>& stars) 00990 { 00991 double minMag = DBL_MAX, maxMag = DBL_MIN; 00992 00993 osg::Vec3Array* coords = new osg::Vec3Array(); 00994 std::vector<StarData>::const_iterator p; 00995 for( p = stars.begin(); p != stars.end(); p++ ) 00996 { 00997 osg::Vec3 v = osg::Vec3(0,_starRadius,0) * 00998 osg::Matrix::rotate( p->declination, 1, 0, 0 ) * 00999 osg::Matrix::rotate( p->right_ascension, 0, 0, 1 ); 01000 01001 coords->push_back( v ); 01002 01003 if ( p->magnitude < minMag ) minMag = p->magnitude; 01004 if ( p->magnitude > maxMag ) maxMag = p->magnitude; 01005 } 01006 01007 osg::Vec4Array* colors = new osg::Vec4Array(); 01008 for( p = stars.begin(); p != stars.end(); p++ ) 01009 { 01010 //float c = 0.5f + 0.5f * ( (p->magnitude-minMag) / (maxMag-minMag) ); 01011 float c = ( (p->magnitude-minMag) / (maxMag-minMag) ); 01012 colors->push_back( osg::Vec4(c,c,c,1.0f) ); 01013 } 01014 01015 osg::Geometry* geometry = new osg::Geometry; 01016 geometry->setUseVertexBufferObjects( true ); 01017 geometry->setVertexArray( coords ); 01018 geometry->setColorArray( colors ); 01019 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); 01020 geometry->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, coords->size())); 01021 01022 osg::StateSet* sset = new osg::StateSet; 01023 01024 sset->setMode( GL_VERTEX_PROGRAM_POINT_SIZE, osg::StateAttribute::ON ); 01025 osg::Program* program = new osg::Program; 01026 program->addShader( new osg::Shader(osg::Shader::VERTEX, s_starVertexSource) ); 01027 program->addShader( new osg::Shader(osg::Shader::FRAGMENT, s_starFragmentSource) ); 01028 sset->setAttributeAndModes( program, osg::StateAttribute::ON ); 01029 01030 sset->setRenderBinDetails( BIN_STARS, "RenderBin"); 01031 sset->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS, 0, 1, false), osg::StateAttribute::ON ); 01032 geometry->setStateSet( sset ); 01033 01034 osg::Geode* starGeode = new osg::Geode; 01035 starGeode->addDrawable( geometry ); 01036 01037 return starGeode; 01038 } 01039 01040 void 01041 SkyNode::getDefaultStars(std::vector<StarData>& out_stars) 01042 { 01043 out_stars.clear(); 01044 01045 for(const char **sptr = s_defaultStarData; *sptr; sptr++) 01046 { 01047 std::stringstream ss(*sptr); 01048 out_stars.push_back(StarData(ss)); 01049 } 01050 } 01051 01052 bool 01053 SkyNode::parseStarFile(const std::string& starFile, std::vector<StarData>& out_stars) 01054 { 01055 out_stars.clear(); 01056 01057 std::fstream in(starFile.c_str()); 01058 if (!in) 01059 { 01060 OE_WARN << "Warning: Unable to open file star file \"" << starFile << "\"" << std::endl; 01061 return false ; 01062 } 01063 01064 while (!in.eof()) 01065 { 01066 std::string line; 01067 01068 std::getline(in, line); 01069 if (in.eof()) 01070 break; 01071 01072 if (line.empty() || line[0] == '#') 01073 continue; 01074 01075 std::stringstream ss(line); 01076 out_stars.push_back(StarData(ss)); 01077 } 01078 01079 in.close(); 01080 01081 return true; 01082 }