/* * Stellarium * Copyright (C) 2003 Fabien Chereau * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "Landscape.hpp" #include "StelApp.hpp" #include "StelTextureMgr.hpp" #include "StelFileMgr.hpp" #include "StelIniParser.hpp" #include "StelLocation.hpp" #include "StelCore.hpp" #include "StelPainter.hpp" #include #include #include Landscape::Landscape(float _radius) : radius(_radius), skyBrightness(1.), angleRotateZOffset(0.) { validLandscape = 0; } Landscape::~Landscape() { } // Load attributes common to all landscapes void Landscape::loadCommon(const QSettings& landscapeIni, const QString& landscapeId) { name = landscapeIni.value("landscape/name").toString(); author = landscapeIni.value("landscape/author").toString(); description = landscapeIni.value("landscape/description").toString(); if (name.isEmpty()) { qWarning() << "No valid landscape definition found for landscape ID " << landscapeId << ". No landscape in use." << endl; validLandscape = 0; return; } else { validLandscape = 1; } // Optional data // Patch GZ: if (landscapeIni.contains("landscape/tesselate_rows")) rows = landscapeIni.value("landscape/tesselate_rows").toInt(); else rows=20; if (landscapeIni.contains("landscape/tesselate_cols")) cols = landscapeIni.value("landscape/tesselate_cols").toInt(); else cols=40; if (landscapeIni.contains("location/planet")) location.planetName = landscapeIni.value("location/planet").toString(); else location.planetName = "Earth"; if (landscapeIni.contains("location/altitude")) location.altitude = landscapeIni.value("location/altitude").toInt(); if (landscapeIni.contains("location/latitude")) location.latitude = StelUtils::getDecAngle(landscapeIni.value("location/latitude").toString())*180./M_PI; if (landscapeIni.contains("location/longitude")) location.longitude = StelUtils::getDecAngle(landscapeIni.value("location/longitude").toString())*180./M_PI; if (landscapeIni.contains("location/country")) location.country = landscapeIni.value("location/country").toString(); if (landscapeIni.contains("location/state")) location.state = landscapeIni.value("location/state").toString(); if (landscapeIni.contains("location/name")) location.name = landscapeIni.value("location/name").toString(); else location.name = name; location.landscapeKey = name; } const QString Landscape::getTexturePath(const QString& basename, const QString& landscapeId) { // look in the landscape directory first, and if not found default to global textures directory QString path; try { path = StelFileMgr::findFile("landscapes/" + landscapeId + "/" + basename); return path; } catch (std::runtime_error& e) { path = StelFileMgr::findFile("textures/" + basename); return path; } } LandscapeOldStyle::LandscapeOldStyle(float _radius) : Landscape(_radius), sideTexs(NULL), sides(NULL), tanMode(false), calibrated(false) {} LandscapeOldStyle::~LandscapeOldStyle() { if (sideTexs) { delete [] sideTexs; sideTexs = NULL; } if (sides) delete [] sides; } void LandscapeOldStyle::load(const QSettings& landscapeIni, const QString& landscapeId) { // TODO: put values into hash and call create method to consolidate code loadCommon(landscapeIni, landscapeId); // Patch GZ: if (landscapeIni.contains("landscape/tesselate_rows")) rows = landscapeIni.value("landscape/tesselate_rows").toInt(); else rows=8; if (landscapeIni.contains("landscape/tesselate_cols")) cols = landscapeIni.value("landscape/tesselate_cols").toInt(); else cols=16; QString type = landscapeIni.value("landscape/type").toString(); if(type != "old_style") { qWarning() << "Landscape type mismatch for landscape " << landscapeId << ", expected old_style, found " << type << ". No landscape in use."; validLandscape = 0; return; } // Load sides textures nbSideTexs = landscapeIni.value("landscape/nbsidetex", 0).toInt(); sideTexs = new StelTextureSP[nbSideTexs]; for (int i=0;i 4) { ++level; slices_inside>>=1; } StelPainter::computeFanDisk(radius, slices_inside, level, groundVertexArr, groundTexCoordArr); // Precompute the vertex arrays for side display static const int stacks = (calibrated ? 16 : 8); // GZ: 8->16, I need better precision. // make slices_per_side=(3<90, // and most likely also not what was intended for other images. // Note that GZ fills this value with a different meaning! const double d_z = calibrated ? decorAltAngle/stacks : (tanMode ? radius*std::tan(decorAltAngle*M_PI/180.f)/stacks : radius*std::sin(decorAltAngle*M_PI/180.0)/stacks); const float alpha = 2.f*M_PI/(nbDecorRepeat*nbSide*slices_per_side); const float ca = std::cos(alpha); const float sa = std::sin(alpha); float y0 = radius*std::cos((angleRotateZ+angleRotateZOffset)*M_PI/180.f); float x0 = radius*std::sin((angleRotateZ+angleRotateZOffset)*M_PI/180.f); LOSSide precompSide; precompSide.arr.primitiveType=StelVertexArray::Triangles; for (int n=0;ngetProjection(StelCore::FrameAltAz)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); painter.enableTexture2d(true); glEnable(GL_CULL_FACE); if (!validLandscape) return; if (drawGroundFirst) drawGround(core, painter); drawDecor(core, painter); if (!drawGroundFirst) drawGround(core, painter); drawFog(core, painter); } // Draw the horizon fog void LandscapeOldStyle::drawFog(StelCore* core, StelPainter& sPainter) const { if (!fogFader.getInterstate()) return; const float vpos = (tanMode||calibrated) ? radius*std::tan(fogAngleShift*M_PI/180.) : radius*std::sin(fogAngleShift*M_PI/180.); sPainter.setProjector(core->getProjection(core->getNavigator()->getAltAzModelViewMat() * Mat4d::translation(Vec3d(0.,0.,vpos)))); glBlendFunc(GL_ONE, GL_ONE); const float nightModeFilter = StelApp::getInstance().getVisionModeNight() ? 0.f : 1.f; sPainter.setColor(fogFader.getInterstate()*(0.1f+0.1f*skyBrightness), fogFader.getInterstate()*(0.1f+0.1f*skyBrightness)*nightModeFilter, fogFader.getInterstate()*(0.1f+0.1f*skyBrightness)*nightModeFilter); fogTex->bind(); const float height = (tanMode||calibrated) ? radius*std::tan(fogAltAngle*M_PI/180.) : radius*std::sin(fogAltAngle*M_PI/180.); sPainter.sCylinder(radius, height, 64, 1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } // Draw the mountains with a few pieces of texture void LandscapeOldStyle::drawDecor(StelCore* core, StelPainter& sPainter) const { // Patched by Georg Zotti: I located an undocumented switch tan_mode, maybe tan_mode=true means cylindrical panorama projection. // anyway, the old code makes unfortunately no sense. // I added a switch "calibrated" for the ini file. If true, it works as this landscape apparently was originally intended. // So I corrected the texture coordinates so that decorAltAngle is the total angle, decorAngleShift the lower angle, // and the texture in between is correctly stretched. // TODO: (1) Replace fog cylinder by similar texture, which could be painted as image layer in Photoshop/Gimp. // (2) Implement calibrated && tan_mode sPainter.setProjector(core->getProjection(StelCore::FrameAltAz)); if (!landFader.getInterstate()) return; const float nightModeFilter = StelApp::getInstance().getVisionModeNight() ? 0.f : 1.f; sPainter.setColor(skyBrightness, skyBrightness*nightModeFilter, skyBrightness*nightModeFilter, landFader.getInterstate()); foreach (const LOSSide& side, precomputedSides) { side.tex->bind(); sPainter.drawSphericalTriangles(side.arr, true, NULL, false); } } // Draw the ground void LandscapeOldStyle::drawGround(StelCore* core, StelPainter& sPainter) const { if (!landFader.getInterstate()) return; const StelNavigator* nav = core->getNavigator(); const float vshift = (tanMode || calibrated) ? radius*std::tan(groundAngleShift*M_PI/180.) : radius*std::sin(groundAngleShift*M_PI/180.); Mat4d mat = nav->getAltAzModelViewMat() * Mat4d::zrotation((groundAngleRotateZ-angleRotateZOffset)*M_PI/180.f) * Mat4d::translation(Vec3d(0,0,vshift)); sPainter.setProjector(core->getProjection(mat)); float nightModeFilter = StelApp::getInstance().getVisionModeNight() ? 0.f : 1.f; sPainter.setColor(skyBrightness, skyBrightness*nightModeFilter, skyBrightness*nightModeFilter, landFader.getInterstate()); groundTex->bind(); sPainter.setArrays((Vec3d*)groundVertexArr.constData(), (Vec2f*)groundTexCoordArr.constData()); sPainter.drawFromArray(StelPainter::Triangles, groundVertexArr.size()/3); } LandscapeFisheye::LandscapeFisheye(float _radius) : Landscape(_radius) {} LandscapeFisheye::~LandscapeFisheye() { } void LandscapeFisheye::load(const QSettings& landscapeIni, const QString& landscapeId) { loadCommon(landscapeIni, landscapeId); QString type = landscapeIni.value("landscape/type").toString(); if(type != "fisheye") { qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected fisheye, found " << type << ". No landscape in use.\n"; validLandscape = 0; return; } create(name, getTexturePath(landscapeIni.value("landscape/maptex").toString(), landscapeId), landscapeIni.value("landscape/texturefov", 360).toFloat(), landscapeIni.value("landscape/angle_rotatez", 0.).toFloat()); } // create a fisheye landscape from basic parameters (no ini file needed) void LandscapeFisheye::create(const QString _name, const QString& _maptex, float atexturefov, float aangleRotateZ) { // qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov; validLandscape = 1; // assume ok... name = _name; mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true)); texFov = atexturefov*M_PI/180.f; angleRotateZ = aangleRotateZ*M_PI/180.f; } void LandscapeFisheye::draw(StelCore* core) { if(!validLandscape) return; if(!landFader.getInterstate()) return; StelNavigator* nav = core->getNavigator(); const StelProjectorP prj = core->getProjection(nav->getAltAzModelViewMat() * Mat4d::zrotation(-(angleRotateZ+(angleRotateZOffset*2*M_PI/360.)))); StelPainter sPainter(prj); // Normal transparency mode glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); float nightModeFilter = StelApp::getInstance().getVisionModeNight() ? 0.f : 1.f; sPainter.setColor(skyBrightness, skyBrightness*nightModeFilter, skyBrightness*nightModeFilter, landFader.getInterstate()); glEnable(GL_CULL_FACE); sPainter.enableTexture2d(true); glEnable(GL_BLEND); mapTex->bind(); // Patch GZ: (40,20)->(cols,rows) sPainter.sSphereMap(radius,cols,rows,texFov,1); glDisable(GL_CULL_FACE); } // spherical panoramas LandscapeSpherical::LandscapeSpherical(float _radius) : Landscape(_radius) {} LandscapeSpherical::~LandscapeSpherical() { } void LandscapeSpherical::load(const QSettings& landscapeIni, const QString& landscapeId) { loadCommon(landscapeIni, landscapeId); QString type = landscapeIni.value("landscape/type").toString(); if (type != "spherical") { qWarning() << "Landscape type mismatch for landscape "<< landscapeId << ", expected spherical, found " << type << ". No landscape in use.\n"; validLandscape = 0; return; } create(name, getTexturePath(landscapeIni.value("landscape/maptex").toString(), landscapeId), landscapeIni.value("landscape/angle_rotatez", 0.f).toFloat()); } // create a spherical landscape from basic parameters (no ini file needed) void LandscapeSpherical::create(const QString _name, const QString& _maptex, float _angleRotateZ) { // qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov; validLandscape = 1; // assume ok... name = _name; mapTex = StelApp::getInstance().getTextureManager().createTexture(_maptex, StelTexture::StelTextureParams(true)); angleRotateZ = _angleRotateZ*M_PI/180.f; } void LandscapeSpherical::draw(StelCore* core) { if(!validLandscape) return; if(!landFader.getInterstate()) return; StelNavigator* nav = core->getNavigator(); const StelProjectorP prj = core->getProjection(nav->getAltAzModelViewMat() * Mat4d::zrotation(-(angleRotateZ+(angleRotateZOffset*2*M_PI/360.)))); StelPainter sPainter(prj); // Normal transparency mode glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); float nightModeFilter = StelApp::getInstance().getVisionModeNight() ? 0. : 1.; sPainter.setColor(skyBrightness, skyBrightness*nightModeFilter, skyBrightness*nightModeFilter, landFader.getInterstate()); glEnable(GL_CULL_FACE); sPainter.enableTexture2d(true); glEnable(GL_BLEND); mapTex->bind(); // TODO: verify that this works correctly for custom projections // seam is at East //sPainter.sSphere(radius, 1.0, 40, 20, 1, true); // GZ: Want better angle resolution, optional! sPainter.sSphere(radius, 1.0, cols, rows, 1, true); glDisable(GL_CULL_FACE); }