Landscape.cpp   Landscape.cpp 
skipping to change at line 23 skipping to change at line 23
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/ */
#include "Landscape.hpp" #include "Landscape.hpp"
#include "StelApp.hpp" #include "StelApp.hpp"
#include "renderer/StelGeometryBuilder.hpp" #include "StelTextureMgr.hpp"
#include "renderer/StelRenderer.hpp"
#include "renderer/StelTextureNew.hpp"
#include "StelFileMgr.hpp" #include "StelFileMgr.hpp"
#include "StelIniParser.hpp" #include "StelIniParser.hpp"
#include "StelLocation.hpp" #include "StelLocation.hpp"
#include "StelCore.hpp" #include "StelCore.hpp"
#include "StelPainter.hpp"
#include <QDebug> #include <QDebug>
#include <QSettings> #include <QSettings>
#include <QVarLengthArray> #include <QVarLengthArray>
#include <QFile>
Landscape::Landscape(float _radius) : radius(_radius), skyBrightness(1.), n #include <QDir>
ightBrightness(0.8), angleRotateZOffset(0.) #include <QtAlgorithms>
Landscape::Landscape(float _radius)
: radius(_radius)
, minBrightness(-1.)
, landscapeBrightness(1.)
, lightScapeBrightness(0.)
, rows(20)
, cols(40)
, angleRotateZ(0.)
, angleRotateZOffset(0.)
, defaultBortleIndex(-1)
, defaultFogSetting(-1)
, defaultExtinctionCoefficient(-1.)
, defaultTemperature(-1000.)
, defaultPressure(-2.)
, horizonPolygon(NULL)
{ {
validLandscape = 0; validLandscape = 0;
} }
Landscape::~Landscape() Landscape::~Landscape()
{ {}
}
// Load attributes common to all landscapes // Load attributes common to all landscapes
void Landscape::loadCommon(const QSettings& landscapeIni, const QString& la ndscapeId) void Landscape::loadCommon(const QSettings& landscapeIni, const QString& la ndscapeId)
{ {
name = landscapeIni.value("landscape/name").toString(); name = landscapeIni.value("landscape/name").toString();
author = landscapeIni.value("landscape/author").toString(); author = landscapeIni.value("landscape/author").toString();
description = landscapeIni.value("landscape/description").toString() ; description = landscapeIni.value("landscape/description").toString() ;
description = description.replace(QRegExp("\\\\n\\s*\\\\n"), "<br /> "); description = description.replace(QRegExp("\\\\n\\s*\\\\n"), "<br /> ");
description = description.replace("\\n", " "); description = description.replace("\\n", " ");
if (name.isEmpty()) if (name.isEmpty())
{ {
qWarning() << "No valid landscape definition found for lands cape ID " qWarning() << "No valid landscape definition (no name) found for landscape ID "
<< landscapeId << ". No landscape in use." << endl; << landscapeId << ". No landscape in use." << endl;
validLandscape = 0; validLandscape = 0;
return; return;
} }
else else
{ {
validLandscape = 1; validLandscape = 1;
} }
// Optional data // Optional data
// Patch GZ: rows = landscapeIni.value("landscape/tesselate_rows", 20).toInt();
if (landscapeIni.contains("landscape/tesselate_rows")) cols = landscapeIni.value("landscape/tesselate_cols", 40).toInt();
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")) if (landscapeIni.childGroups().contains("location"))
location.planetName = landscapeIni.value("location/planet"). {
toString(); if (landscapeIni.contains("location/planet"))
else location.planetName = landscapeIni.value("location/p
location.planetName = "Earth"; lanet").toString();
if (landscapeIni.contains("location/altitude")) else
location.altitude = landscapeIni.value("location/altitude"). location.planetName = "Earth";
toInt(); if (landscapeIni.contains("location/altitude"))
if (landscapeIni.contains("location/latitude")) location.altitude = landscapeIni.value("location/alt
location.latitude = StelUtils::getDecAngle(landscapeIni.valu itude").toInt();
e("location/latitude").toString())*180./M_PI; if (landscapeIni.contains("location/latitude"))
if (landscapeIni.contains("location/longitude")) location.latitude = StelUtils::getDecAngle(landscape
location.longitude = StelUtils::getDecAngle(landscapeIni.val Ini.value("location/latitude").toString())*180./M_PI;
ue("location/longitude").toString())*180./M_PI; if (landscapeIni.contains("location/longitude"))
if (landscapeIni.contains("location/country")) location.longitude = StelUtils::getDecAngle(landscap
location.country = landscapeIni.value("location/country").to eIni.value("location/longitude").toString())*180./M_PI;
String(); if (landscapeIni.contains("location/country"))
if (landscapeIni.contains("location/state")) location.country = landscapeIni.value("location/coun
location.state = landscapeIni.value("location/state").toStri try").toString();
ng(); if (landscapeIni.contains("location/state"))
if (landscapeIni.contains("location/name")) location.state = landscapeIni.value("location/state"
location.name = landscapeIni.value("location/name").toString ).toString();
(); if (landscapeIni.contains("location/name"))
else location.name = landscapeIni.value("location/name").
location.name = name; toString();
location.landscapeKey = name; else
// New entries by GZ. location.name = name;
if (landscapeIni.contains("location/light_pollution")) location.landscapeKey = name;
defaultBortleIndex = landscapeIni.value("location/light_poll defaultBortleIndex = landscapeIni.value("location/light_poll
ution").toInt(); ution", -1).toInt();
else defaultBortleIndex=-1; // mark "invalid/no change". if (defaultBortleIndex<=0) defaultBortleIndex=-1; // neg. va
if (defaultBortleIndex<=0) defaultBortleIndex=-1; // also allow neg. lues in ini file signal "no change".
values in ini file, signalling "no change". if (defaultBortleIndex>9) defaultBortleIndex=9; // correct b
if (defaultBortleIndex>9) defaultBortleIndex=9; // correct bad value ad values.
s.
defaultFogSetting = landscapeIni.value("location/display_fog
defaultFogSetting = landscapeIni.value("location/display_fog", -1).t ", -1).toInt();
oInt(); defaultExtinctionCoefficient = landscapeIni.value("location/
defaultExtinctionCoefficient = landscapeIni.value("location/atmosphe atmospheric_extinction_coefficient", -1.0).toDouble();
ric_extinction_coefficient", -1.0).toDouble(); defaultTemperature = landscapeIni.value("location/atmospheri
defaultTemperature = landscapeIni.value("location/atmospheric_temper c_temperature", -1000.0).toDouble();
ature", -1000.0).toDouble(); defaultPressure = landscapeIni.value("location/atmospheric_p
defaultPressure = landscapeIni.value("location/atmospheric_pressure" ressure", -2.0).toDouble(); // -2=no change! [-1=computeFromAltitude]
, -2.0).toDouble(); }
// Set night brightness for landscape
defaultBrightness = landscapeIni.value("landscape/initial_brightness // Set minimal brightness for landscape
", -1.0).toDouble(); minBrightness = landscapeIni.value("landscape/minimal_brightness", -
} 1.0).toDouble();
// This is now optional for all classes, for mixing with a photo hor
izon:
// they may have different offsets, like a south-centered pano and a
geographically-oriented polygon.
// In case they are aligned, we can use one value angle_rotatez, or
define the polygon rotation individually.
if (landscapeIni.contains("landscape/polygonal_horizon_list"))
{
createPolygonalHorizon(
StelFileMgr::findFile("landscapes/"
+ landscapeId + "/" + landscapeIni.value("landscape/polygonal_horizon_list"
).toString()),
landscapeIni.value("landscape/polygo
nal_angle_rotatez", 0.f).toFloat(),
landscapeIni.value("landscape/polygo
nal_horizon_list_mode", "azDeg_altDeg").toString());
// This line can then be drawn in all classes with the color
specified here. If not specified, don't draw it! (flagged by negative red)
horizonPolygonLineColor=StelUtils::strToVec3f( landscapeIni.
value("landscape/horizon_line_color", "-1,0,0" ).toString());
}
}
void Landscape::createPolygonalHorizon(const QString& lineFileName, const f
loat polyAngleRotateZ, const QString &listMode )
{
// qDebug() << _name << " " << _fullpath << " " << _lineFileName ;
QStringList horizonModeList;
horizonModeList << "azDeg_altDeg" << "azDeg_zdDeg" << "azRad_altRad"
<< "azRad_zdRad" << "azGrad_zdGrad" << "azGrad_zdGrad";
const horizonListMode coordMode=(horizonListMode) horizonModeList.in
dexOf(listMode);
#include <iostream> QVector<Vec3d> horiPoints(0);
const QString Landscape::getTexturePath(const QString& basename, const QStr QFile file(lineFileName);
ing& landscapeId)
{ if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
// look in the landscape directory first, and if not found default t
o global textures directory
QString path;
try
{ {
path = StelFileMgr::findFile("landscapes/" + landscapeId + " qWarning() << "Landscape Horizon line data file" << QDir::to
/" + basename); NativeSeparators(lineFileName) << "not found.";
return path; return;
} }
catch (std::runtime_error& e) QRegExp emptyLine("^\\s*$");
{ QTextStream in(&file);
#ifdef BUILD_FOR_MAEMO while (!in.atEnd())
if (!basename.endsWith(".pvr")) {
// Build list of vertices. The checks can certainly become m
ore robust.
QString line = in.readLine();
if (line.length()==0) continue;
if (emptyLine.exactMatch((line))) continue;
if (line.at(0)=='#') continue; // skip comment lines.
//QStringList list = line.split(QRegExp("\\b\\s+\\b"));
QStringList list = line.trimmed().split(QRegExp("\\s+"));
if (list.count() < 2)
{ {
QString tmp = basename; qWarning() << "Landscape polygon file" << QDir::toNa
tmp.replace(".png", ".pvr"); tiveSeparators(lineFileName) << "has bad line:" << line << "with" << list.c
try ount() << "elements";
{ continue;
tmp = getTexturePath(tmp, landscapeId);
tmp.replace(".pvr", ".png");
return tmp;
}
catch (std::runtime_error& e)
{;}
} }
#endif //if (list.count() > 2) // use first two elements but give w
path = StelFileMgr::findFile("textures/" + basename); arning.
return path; //{
// qWarning() << "Landscape polygon file" << QDir::toNa
tiveSeparators(lineFileName) << "has excessive elements in line:" << line <
< " (" << list.count() << ", not 2 elements)";
//}
Vec3d point;
//qDebug() << "Creating point for az=" << list.at(0) << " al
t/zd=" << list.at(1);
float az = 0.f, alt = 0.f;
switch (coordMode)
{
case azDeg_altDeg:
az=(180.0f - polyAngleRotateZ - list.at(0).t
oFloat())*M_PI/180.f;
alt=list.at(1).toFloat()*M_PI/180.f;
break;
case azDeg_zdDeg:
az=(180.0f - polyAngleRotateZ - list.at(0).t
oFloat())*M_PI/180.f;
alt=(90.0f-list.at(1).toFloat())*M_PI/180.f;
break;
case azRad_altRad:
az=(M_PI - polyAngleRotateZ*M_PI/180.f - lis
t.at(0).toFloat());
alt=list.at(1).toFloat();
break;
case azRad_zdRad:
az=(M_PI - polyAngleRotateZ*M_PI/180.f - lis
t.at(0).toFloat());
alt=M_PI/2.f-list.at(1).toFloat();
break;
case azGrad_altGrad:
az=(200.0f - list.at(0).toFloat())*M_PI/200
.f - polyAngleRotateZ*M_PI/180.f;
alt=list.at(1).toFloat()*M_PI/200.f;
break;
case azGrad_zdGrad:
az=(200.0f - list.at(0).toFloat())*M_PI/200
.f - polyAngleRotateZ*M_PI/180.f;
alt=(100.0f-list.at(1).toFloat())*M_PI/200.f
;
break;
default: qWarning() << "invalid coordMode while read
ing horizon line.";
}
StelUtils::spheToRect(az, alt, point);
horiPoints.append(point);
} }
file.close();
//horiPoints.append(horiPoints.at(0)); // close loop? Apparently not
necessary.
//qDebug() << "created horiPoints with " << horiPoints.count() << "p
oints:";
//for (int i=0; i<horiPoints.count(); ++i)
// qDebug() << horiPoints.at(i)[0] << "/" << horiPoints.at(i)[1
] << "/" << horiPoints.at(i)[2] ;
AllSkySphericalRegion allskyRegion;
SphericalPolygon aboveHorizonPolygon;
aboveHorizonPolygon.setContour(horiPoints);
horizonPolygon = allskyRegion.getSubtraction(aboveHorizonPolygon);
}
#include <iostream>
const QString Landscape::getTexturePath(const QString& basename, const QStr
ing& landscapeId) const
{
// look in the landscape directory first, and if not found default t
o global textures directory
QString path = StelFileMgr::findFile("landscapes/" + landscapeId + "
/" + basename);
if (path.isEmpty())
path = StelFileMgr::findFile("textures/" + basename);
return path;
} }
LandscapeOldStyle::LandscapeOldStyle(float _radius) LandscapeOldStyle::LandscapeOldStyle(float _radius)
: Landscape(_radius) : Landscape(_radius)
, sideTexs(NULL) , sideTexs(NULL)
, texturesInitialized(false) , nbSideTexs(0)
, nbSide(0)
, sides(NULL) , sides(NULL)
, fogTex(NULL) , nbDecorRepeat(0)
, groundTex(NULL) , fogAltAngle(0.)
, fogAngleShift(0.)
, decorAltAngle(0.)
, decorAngleShift(0.)
, groundAngleShift(0.)
, groundAngleRotateZ(0.)
, drawGroundFirst(0)
, tanMode(false) , tanMode(false)
, calibrated(false) , calibrated(false)
, fogCylinderBuffer(NULL)
// Way off to ensure the height is detected as "changed" on the firs
t drawFog() call.
, previousFogHeight(-1000.0f)
, groundFanDisk(NULL)
, groundFanDiskIndices(NULL)
{} {}
LandscapeOldStyle::~LandscapeOldStyle() LandscapeOldStyle::~LandscapeOldStyle()
{ {
if (NULL != sideTexs) if (sideTexs)
{ {
if(texturesInitialized)
{
for(int i = 0; i < nbSideTexs; ++i)
{
delete sideTexs[i].texture;
}
}
delete [] sideTexs; delete [] sideTexs;
sideTexs = NULL; sideTexs = NULL;
} }
if(NULL != fogTex) if (sides) delete [] sides;
{ if (sidesImages.size()>0)
delete fogTex;
fogTex = NULL;
}
if(NULL != groundTex)
{
delete groundTex;
groundTex = NULL;
}
if (NULL != fogCylinderBuffer)
{
delete fogCylinderBuffer;
fogCylinderBuffer = NULL;
}
if (NULL != groundFanDisk)
{
Q_ASSERT_X(NULL != groundFanDiskIndices, Q_FUNC_INFO,
"Vertex buffer is generated but index buffer is n
ot");
delete groundFanDisk;
groundFanDisk = NULL;
delete groundFanDiskIndices;
groundFanDiskIndices = NULL;
}
if (NULL != sides)
{ {
delete [] sides; qDeleteAll(sidesImages);
sides = NULL; sidesImages.clear();
} }
for(int side = 0; side < precomputedSides.length(); ++side)
{
delete precomputedSides[side].vertices;
delete precomputedSides[side].indices;
}
precomputedSides.clear();
} }
void LandscapeOldStyle::load(const QSettings& landscapeIni, const QString& landscapeId) void LandscapeOldStyle::load(const QSettings& landscapeIni, const QString& landscapeId)
{ {
// TODO: put values into hash and call create method to consolidate code // TODO: put values into hash and call create() method to consolidat e code
loadCommon(landscapeIni, landscapeId); loadCommon(landscapeIni, landscapeId);
// rows, cols have been loaded already, but with different defaults.
if (landscapeIni.contains("landscape/tesselate_rows")) // GZ Hey, they are not used altogether! Resolution is constant, bel
rows = landscapeIni.value("landscape/tesselate_rows").toInt( ow!
); //rows = landscapeIni.value("landscape/tesselate_rows", 8).toInt();
else rows=8; //cols = landscapeIni.value("landscape/tesselate_cols", 16).toInt();
if (landscapeIni.contains("landscape/tesselate_cols"))
cols = landscapeIni.value("landscape/tesselate_cols").toInt(
);
else cols=16;
QString type = landscapeIni.value("landscape/type").toString(); QString type = landscapeIni.value("landscape/type").toString();
if(type != "old_style") if(type != "old_style")
{ {
qWarning() << "Landscape type mismatch for landscape " << la ndscapeId qWarning() << "Landscape type mismatch for landscape " << la ndscapeId
<< ", expected old_style, found " << type << ". No landscape in use."; << ", expected old_style, found " << type << ". No landscape in use.";
validLandscape = 0; validLandscape = 0;
return; return;
} }
// Load sides textures // Load sides textures
nbSideTexs = landscapeIni.value("landscape/nbsidetex", 0).toInt(); nbSideTexs = landscapeIni.value("landscape/nbsidetex", 0).toInt();
sideTexs = new SideTexture[nbSideTexs]; sideTexs = new StelTextureSP[nbSideTexs];
for (int i=0; i<nbSideTexs; ++i) for (int i=0; i<nbSideTexs; ++i)
{ {
QString textureKey = QString("landscape/tex%1").arg(i); QString textureKey = QString("landscape/tex%1").arg(i);
QString textureName = landscapeIni.value(textureKey).toStrin g(); QString textureName = landscapeIni.value(textureKey).toStrin g();
sideTexs[i].path = getTexturePath(textureName, landscapeId); const QString texturePath = getTexturePath(textureName, land
// Will be lazily initialized scapeId);
sideTexs[i].texture = NULL; sideTexs[i] = StelApp::getInstance().getTextureManager().cre
ateTexture(texturePath);
// GZ: To query the textures, also fill an array of QImage*,
but only
// if that query is not going to be prevented by the polygon
that already has been loaded at that point...
if ( (!horizonPolygon) && calibrated ) { // for uncalibrated
landscapes the texture is currently never queried, so no need to store.
QImage *image = new QImage(texturePath);
sidesImages.append(image); // indices identical to t
hose in sideTexs
}
} }
if ( (!horizonPolygon) && calibrated )
{
Q_ASSERT(sidesImages.size()==nbSideTexs);
}
QMap<int, int> texToSide;
// Init sides parameters // Init sides parameters
nbSide = landscapeIni.value("landscape/nbside", 0).toInt(); nbSide = landscapeIni.value("landscape/nbside", 0).toInt();
sides = new landscapeTexCoord[nbSide]; sides = new landscapeTexCoord[nbSide];
int texnum; int texnum;
for (int i=0;i<nbSide;++i) for (int i=0;i<nbSide;++i)
{ {
QString key = QString("landscape/side%1").arg(i); QString key = QString("landscape/side%1").arg(i);
QString description = landscapeIni.value(key).toString(); QString description = landscapeIni.value(key).toString();
//sscanf(s.toLocal8Bit(),"tex%d:%f:%f:%f:%f",&texnum,&a,&b,& c,&d); //sscanf(s.toLocal8Bit(),"tex%d:%f:%f:%f:%f",&texnum,&a,&b,& c,&d);
QStringList parameters = description.split(':'); QStringList parameters = description.split(':');
//TODO: How should be handled an invalid texture description ? //TODO: How should be handled an invalid texture description ?
QString textureName = parameters.value(0); QString textureName = parameters.value(0);
texnum = textureName.right(textureName.length() - 3).toInt() ; texnum = textureName.right(textureName.length() - 3).toInt() ;
sides[i].tex = sideTexs[texnum];
sides[i].texCoords[0] = parameters.at(1).toFloat(); sides[i].texCoords[0] = parameters.at(1).toFloat();
sides[i].texCoords[1] = parameters.at(2).toFloat(); sides[i].texCoords[1] = parameters.at(2).toFloat();
sides[i].texCoords[2] = parameters.at(3).toFloat(); sides[i].texCoords[2] = parameters.at(3).toFloat();
sides[i].texCoords[3] = parameters.at(4).toFloat(); sides[i].texCoords[3] = parameters.at(4).toFloat();
//qDebug() << i << texnum << sides[i].texCoords[0] << sides[ i].texCoords[1] << sides[i].texCoords[2] << sides[i].texCoords[3]; //qDebug() << i << texnum << sides[i].texCoords[0] << sides[ i].texCoords[1] << sides[i].texCoords[2] << sides[i].texCoords[3];
// Prior to precomputing the sides, we used to match E to si // Prior to precomputing the sides, we used to match E to si
de0 de0.
// in r4598 the precomputing was put in place and caused a p // In r4598 the precomputing was put in place and caused a p
roblem for roblem for
// old_style landscapes which had a z rotation on the side t extures // old_style landscapes which had a z rotation on the side t extures
// and where side0 did not map to tex0 // and where side0 did not map to tex0
// texToSide is a nasty hack to replace the old behaviour // texToSide is a nasty hack to replace the old behaviour.
// GZ for V0.13: I put the zrotation to the draw call (like
for all other landscapes).
// Maybe this can be again simplified?
texToSide[i] = texnum; texToSide[i] = texnum;
} }
nbDecorRepeat = landscapeIni.value("landscape/nb_decor_repeat", 1).t
oInt();
QString groundTexName = landscapeIni.value("landscape/groundtex").to String(); QString groundTexName = landscapeIni.value("landscape/groundtex").to String();
groundTexPath = getTexturePath(groundTexName, landscapeId); QString groundTexPath = getTexturePath(groundTexName, landscapeId);
QString description = landscapeIni.value("landscape/ground").toStrin groundTex = StelApp::getInstance().getTextureManager().createTexture
g(); (groundTexPath, StelTexture::StelTextureParams(true));
//sscanf(description.toLocal8Bit(),"groundtex:%f:%f:%f:%f",&a,&b,&c, // GZ 2013/11: I don't see any use of this:
&d); // QString description = landscapeIni.value("landscape/ground").toStrin
QStringList parameters = description.split(':'); g();
groundTexCoord.texCoords[0] = parameters.at(1).toFloat(); // //sscanf(description.toLocal8Bit(),"groundtex:%f:%f:%f:%f",&a,&b,&c,
groundTexCoord.texCoords[1] = parameters.at(2).toFloat(); &d);
groundTexCoord.texCoords[2] = parameters.at(3).toFloat(); // QStringList parameters = description.split(':');
groundTexCoord.texCoords[3] = parameters.at(4).toFloat(); // groundTexCoord.tex = groundTex;
// groundTexCoord.texCoords[0] = parameters.at(1).toFloat();
// groundTexCoord.texCoords[1] = parameters.at(2).toFloat();
// groundTexCoord.texCoords[2] = parameters.at(3).toFloat();
// groundTexCoord.texCoords[3] = parameters.at(4).toFloat();
QString fogTexName = landscapeIni.value("landscape/fogtex").toString (); QString fogTexName = landscapeIni.value("landscape/fogtex").toString ();
fogTexPath = getTexturePath(fogTexName, landscapeId); QString fogTexPath = getTexturePath(fogTexName, landscapeId);
description = landscapeIni.value("landscape/fog").toString(); fogTex = StelApp::getInstance().getTextureManager().createTexture(fo
//sscanf(description.toLocal8Bit(),"fogtex:%f:%f:%f:%f",&a,&b,&c,&d) gTexPath, StelTexture::StelTextureParams(true, GL_LINEAR, GL_REPEAT));
; // GZ 2013/11: I don't see any use of this:
parameters = description.split(':'); // QString description = landscapeIni.value("landscape/fog").toString()
fogTexCoord.texCoords[0] = parameters.at(1).toFloat(); ;
fogTexCoord.texCoords[1] = parameters.at(2).toFloat(); // //sscanf(description.toLocal8Bit(),"fogtex:%f:%f:%f:%f",&a,&b,&c,&d)
fogTexCoord.texCoords[2] = parameters.at(3).toFloat(); ;
fogTexCoord.texCoords[3] = parameters.at(4).toFloat(); // QStringList parameters = description.split(':');
// fogTexCoord.tex = fogTex;
// fogTexCoord.texCoords[0] = parameters.at(1).toFloat();
// fogTexCoord.texCoords[1] = parameters.at(2).toFloat();
// fogTexCoord.texCoords[2] = parameters.at(3).toFloat();
// fogTexCoord.texCoords[3] = parameters.at(4).toFloat();
nbDecorRepeat = landscapeIni.value("landscape/nb_decor_repeat", 1).toInt();
fogAltAngle = landscapeIni.value("landscape/fog_alt_angle", 0 .).toFloat(); fogAltAngle = landscapeIni.value("landscape/fog_alt_angle", 0 .).toFloat();
fogAngleShift = landscapeIni.value("landscape/fog_angle_shift", 0.).toFloat(); fogAngleShift = landscapeIni.value("landscape/fog_angle_shift", 0.).toFloat();
decorAltAngle = landscapeIni.value("landscape/decor_alt_angle", 0.).toFloat(); decorAltAngle = landscapeIni.value("landscape/decor_alt_angle", 0.).toFloat();
decorAngleShift = landscapeIni.value("landscape/decor_angle_shift ", 0.).toFloat(); decorAngleShift = landscapeIni.value("landscape/decor_angle_shift ", 0.).toFloat();
angleRotateZ = landscapeIni.value("landscape/decor_angle_rotat angleRotateZ = landscapeIni.value("landscape/decor_angle_rotat
ez", 0.).toFloat(); ez", 0.).toFloat() * M_PI/180.f;
groundAngleShift = landscapeIni.value("landscape/ground_angle_shif groundAngleShift = landscapeIni.value("landscape/ground_angle_shif
t", 0.).toFloat(); t", 0.).toFloat() * M_PI/180.f;
groundAngleRotateZ = landscapeIni.value("landscape/ground_angle_rota groundAngleRotateZ = landscapeIni.value("landscape/ground_angle_rota
tez", 0.).toFloat(); tez", 0.).toFloat() * M_PI/180.f;
drawGroundFirst = landscapeIni.value("landscape/draw_ground_first ", 0).toInt(); drawGroundFirst = landscapeIni.value("landscape/draw_ground_first ", 0).toInt();
tanMode = landscapeIni.value("landscape/tan_mode", false) .toBool(); tanMode = landscapeIni.value("landscape/tan_mode", false) .toBool();
calibrated = landscapeIni.value("landscape/calibrated", fals e).toBool(); calibrated = landscapeIni.value("landscape/calibrated", fals e).toBool();
}
void LandscapeOldStyle::draw(StelCore* core, StelRenderer* renderer)
{
if (!validLandscape) {return;}
lazyInitTextures(renderer);
renderer->setBlendMode(BlendMode_Alpha);
StelProjectorP projector =
core->getProjection(StelCore::FrameAltAz, StelCore::Refracti
onOff);
renderer->setCulledFaces(CullFace_Back);
if (drawGroundFirst) {drawGround(core, renderer);}
drawDecor(core, renderer);
if (!drawGroundFirst) {drawGround(core, renderer);}
drawFog(core, renderer);
renderer->setCulledFaces(CullFace_None);
}
// Draw the horizon fog
void LandscapeOldStyle::drawFog(StelCore* core, StelRenderer* renderer)
{
if (!fogFader.getInterstate()) {return;}
const float vpos = radius * ((tanMode || calibrated)
? std::tan(fogAngleShift * M_PI / 180.0)
: std::sin(fogAngleShift * M_PI / 180.0));
StelProjector::ModelViewTranformP transform =
core->getAltAzModelViewTransform(StelCore::RefractionOff);
transform->combine(Mat4d::translation(Vec3d(0.,0.,vpos)));
StelProjectorP projector = core->getProjection(transform); // Precompute the vertex arrays for ground display
renderer->setBlendMode(BlendMode_Add); // Make slices_per_side=(3<<K) so that the innermost polygon of the
fandisk becomes a triangle:
//const int slices_per_side = 3*64/(nbDecorRepeat*nbSide);
//if (slices_per_side<=0) // GZ: How can negative ever happen?
// slices_per_side = 1;
const int slices_per_side = qMax(3*64/(nbDecorRepeat*nbSide), 1);
const float nightModeFilter = // draw a fan disk instead of a ordinary disk to that the inner slic
StelApp::getInstance().getVisionModeNight() ? 0.f : 1.f; es
// are not so slender. When they are too slender, culling errors occ
const float intensity = fogFader.getInterstate() * (0.1f + 0.1f * sk ur
yBrightness); // in cylinder projection mode.
const float filteredIntensity = intensity * nightModeFilter; int slices_inside = nbSide*slices_per_side*nbDecorRepeat;
renderer->setGlobalColor(intensity, filteredIntensity, filteredInten int level = 0;
sity); while ((slices_inside&1)==0 && slices_inside > 4)
fogTex->bind();
const float height = (tanMode || calibrated)
? radius * std::tan(fogAltAngle * M_PI / 180.f)
: radius * std::sin(fogAltAngle * M_PI / 180.f);
if(std::fabs(height - previousFogHeight) > 0.01)
{
// Height has changed, need to regenerate the buffer.
delete fogCylinderBuffer;
fogCylinderBuffer = NULL;
previousFogHeight = height;
}
if(NULL == fogCylinderBuffer)
{ {
fogCylinderBuffer = renderer->createVertexBuffer<VertexP3T2> ++level;
(PrimitiveType_TriangleStrip); slices_inside>>=1;
StelGeometryBuilder().buildCylinder(fogCylinderBuffer, radiu
s, height, 64, true);
} }
StelPainter::computeFanDisk(radius, slices_inside, level, groundVert exArr, groundTexCoordArr);
renderer->drawVertexBuffer(fogCylinderBuffer, NULL, projector); // Precompute the vertex arrays for side display. The geometry of th
e sides is always a cylinder.
// The texture is split into regular quads.
renderer->setBlendMode(BlendMode_Alpha); // GZ: the old code for vertical placement makes unfortunately no se
} nse. There are many approximately-fitted landscapes, though.
// I added a switch "calibrated" for the ini file. If true, it works
void LandscapeOldStyle::lazyInitTextures(StelRenderer* renderer) as this landscape apparently was originally intended.
{ // So I corrected the texture coordinates so that decorAltAngle is t
if(texturesInitialized){return;} he total vertical angle, decorAngleShift the lower angle,
for (int i = 0; i < nbSideTexs; ++i) // and the texture in between is correctly stretched.
// I located an undocumented switch tan_mode, maybe tan_mode=true me
ans cylindrical panorama projection.
// Since V0.13, calibrated&&tanMode also works!
// In calibrated && !tan_mode, the vertical position is computed cor
rectly, so that quads off the horizon are larger.
// in calibrated && tan_mode, d_z can become a constant because the
texture is already predistorted in cylindrical projection.
static const int stacks = (calibrated ? 16 : 8); // GZ: 8->16, I nee
d better precision.
float z0, d_z;
if (calibrated)
{ {
sideTexs[i].texture = renderer->createTexture(sideTexs[i].pa if (tanMode) // cylindrical pano: linear in d_z, simpler.
th); {
z0=radius*std::tan(decorAngleShift*M_PI/180.0f);
d_z=(radius*std::tan((decorAltAngle+decorAngleShift)
*M_PI/180.0f) - z0)/stacks;
}
else // equirectangular pano: angular z, requires more work
in the loop below!
{
z0=decorAngleShift;
d_z=decorAltAngle/stacks;
}
} }
for (int i=0;i<nbSide;++i) else // buggy code, but legacy.
{ {
sides[i].tex = sideTexs[i].texture; z0 =(tanMode ? radius * std::tan(decorAngleShift*M_PI/180.f)
: radius * std::sin(decorAngleShift*M_PI/180.f));
d_z=(tanMode ? radius * std::tan(decorAltAngle *M_PI/180.f)
/stacks : radius * std::sin(decorAltAngle *M_PI/180.0)/stacks);
} }
fogTex = renderer->createTexture(fogTexPath,
TextureParams().generateMipmap
s().wrap(TextureWrap_Repeat));
fogTexCoord.tex = fogTex;
groundTex = renderer->createTexture(groundTexPath, TextureParams().g
enerateMipmaps());
groundTexCoord.tex = groundTex;
texturesInitialized = true;
}
void LandscapeOldStyle::generatePrecomputedSides(StelRenderer* renderer)
{
// Make slicesPerSide=(3<<K) so that the innermost polygon of the fa
ndisk becomes a triangle:
const int slicesPerSide = std::max(1, 3 * 64 / (nbDecorRepeat * nbSi
de));
// Precompute the vertex arrays for side display const float alpha = 2.f*M_PI/(nbDecorRepeat*nbSide*slices_per_side);
static const int stacks = (calibrated ? 16 : 8); // GZ: 8->16, I nee //delta_azimuth
d better precision.
const double z0 = calibrated ?
// GZ: For calibrated, we use z=decorAngleShift...(decorAltAngle-dec
orAngleShift), but we must compute the tan in the loop.
decorAngleShift : (tanMode ? radius * std::tan(decorAngleShift*M_PI/
180.f) : radius * std::sin(decorAngleShift*M_PI/180.f));
// GZ: The old formula is completely meaningless for photos with ope
ning angle >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 ? ra
dius*std::tan(decorAltAngle*M_PI/180.f)/stacks : radius*std::sin(decorAltAn
gle*M_PI/180.0)/stacks);
const float alpha = 2.f * M_PI / (nbDecorRepeat * nbSide * slicesPer
Side);
const float ca = std::cos(alpha); const float ca = std::cos(alpha);
const float sa = std::sin(alpha); const float sa = std::sin(alpha);
float y0 = radius*std::cos(angleRotateZ*M_PI/180.f); //float y0 = radius*std::cos(angleRotateZ);
float x0 = radius*std::sin(angleRotateZ*M_PI/180.f); //float x0 = radius*std::sin(angleRotateZ);
float y0 = radius;
float x0 = 0.0f;
LOSSide precompSide; LOSSide precompSide;
for (int n = 0; n < nbDecorRepeat; n++) precompSide.arr.primitiveType=StelVertexArray::Triangles;
for (int n=0;n<nbDecorRepeat;n++)
{ {
for (int i = 0; i < nbSide; i++) for (int i=0;i<nbSide;i++)
{ {
if (!texToSide.contains(i)) int ti;
if (texToSide.contains(i))
ti = texToSide[i];
else
{ {
qDebug() << QString("LandscapeOldStyle::load ERROR: found no corresponding tex value for side%1").arg(i); qDebug() << QString("LandscapeOldStyle::load ERROR: found no corresponding tex value for side%1").arg(i);
break; break;
} }
precompSide.arr.vertex.resize(0);
const int ti = texToSide[i]; precompSide.arr.texCoords.resize(0);
precompSide.vertices = precompSide.arr.indices.resize(0);
renderer->createVertexBuffer<VertexP3T2>(Pri precompSide.tex=sideTexs[ti];
mitiveType_Triangles);
precompSide.indices = renderer->createIndexBuffer(In
dexType_U16);
precompSide.tex = sideTexs[ti].texture;
float tx0 = sides[ti].texCoords[0]; float tx0 = sides[ti].texCoords[0];
const float d_tx0 = (sides[ti].texCoords[2] - sides[ const float d_tx = (sides[ti].texCoords[2]-sides[ti]
ti].texCoords[0]) / slicesPerSide; .texCoords[0]) / slices_per_side;
const float d_ty = (sides[ti].texCoords[3] - sides[ const float d_ty = (sides[ti].texCoords[3]-sides[ti]
ti].texCoords[1]) / stacks; .texCoords[1]) / stacks;
for (int j = 0; j < slicesPerSide; j++) for (int j=0;j<slices_per_side;j++)
{ {
const float y1 = y0 * ca - x0 * sa; const float y1 = y0*ca - x0*sa;
const float x1 = y0 * sa + x0 * ca; const float x1 = y0*sa + x0*ca;
const float tx1 = tx0 + d_tx0; const float tx1 = tx0 + d_tx;
float z = z0;
float z = z0;
float ty0 = sides[ti].texCoords[1]; float ty0 = sides[ti].texCoords[1];
for (int k=0;k<=stacks*2;k+=2)
for (int k = 0; k <= stacks * 2; k += 2)
{ {
precompSide.arr.texCoords << Vec2f(t
const float calibratedZ = x0, ty0) << Vec2f(tx1, ty0);
calibrated ? radius * std::t if (calibrated && !tanMode)
an(z * M_PI / 180.0f) : z; {
precompSide.vertices->addVertex float tanZ=radius * std::tan
(VertexP3T2(Vec3f(x0, y0, ca (z*M_PI/180.f);
libratedZ), Vec2f(tx0, ty0))); precompSide.arr.vertex << Ve
precompSide.vertices->addVertex c3d(x0, y0, tanZ) << Vec3d(x1, y1, tanZ);
(VertexP3T2(Vec3f(x1, y1, ca } else
libratedZ), Vec2f(tx1, ty0))); {
z += d_z; precompSide.arr.vertex << Ve
c3d(x0, y0, z) << Vec3d(x1, y1, z);
}
z += d_z;
ty0 += d_ty; ty0 += d_ty;
} }
const uint offset = j*(stacks+1)*2; unsigned int offset = j*(stacks+1)*2;
for (int k = 2; k < stacks * 2 + 2; k += 2) for (int k = 2;k<stacks*2+2;k+=2)
{ {
precompSide.indices->addIndex(offset precompSide.arr.indices << offset+k-
+ k - 2); 2 << offset+k-1 << offset+k;
precompSide.indices->addIndex(offset precompSide.arr.indices << offset+k
+ k - 1); << offset+k-1 << offset+k+1;
precompSide.indices->addIndex(offset
+ k);
precompSide.indices->addIndex(offset
+ k);
precompSide.indices->addIndex(offset
+ k - 1);
precompSide.indices->addIndex(offset
+ k + 1);
} }
y0 = y1; y0 = y1;
x0 = x1; x0 = x1;
tx0 = tx1; tx0 = tx1;
} }
precompSide.vertices->lock();
precompSide.indices->lock();
precomputedSides.append(precompSide); precomputedSides.append(precompSide);
} }
} }
} }
// Draw the mountains with a few pieces of texture void LandscapeOldStyle::draw(StelCore* core)
void LandscapeOldStyle::drawDecor(StelCore* core, StelRenderer* renderer)
{ {
if (!landFader.getInterstate()) {return;} StelPainter painter(core->getProjection(StelCore::FrameAltAz, StelCo
re::RefractionOff));
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
painter.enableTexture2d(true);
glEnable(GL_CULL_FACE);
// Patched by Georg Zotti: I located an undocumented switch tan_mode if (!validLandscape)
, maybe tan_mode=true means cylindrical panorama projection. return;
// anyway, the old code makes unfortunately no sense. if (drawGroundFirst)
// I added a switch "calibrated" for the ini file. If true, it works drawGround(core, painter);
as this landscape apparently was originally intended. drawDecor(core, painter);
// So I corrected the texture coordinates so that decorAltAngle is t if (!drawGroundFirst)
he total angle, decorAngleShift the lower angle, drawGround(core, painter);
// and the texture in between is correctly stretched. drawFog(core, painter);
// TODO: (1) Replace fog cylinder by similar texture, which could be
painted as image layer in Photoshop/Gimp. // If a horizon line also has been defined, draw it.
// (2) Implement calibrated && tan_mode if (horizonPolygon && (horizonPolygonLineColor[0] >= 0))
StelProjector::ModelViewTranformP transform = {
core->getAltAzModelViewTransform(StelCore::RefractionOff); //qDebug() << "drawing line";
transform->combine(Mat4d::zrotation(-angleRotateZOffset*M_PI/180.f)) StelProjector::ModelViewTranformP transfo = core->getAltAzMo
; delViewTransform(StelCore::RefractionOff);
StelProjectorP projector = core->getProjection(transform); transfo->combine(Mat4d::zrotation(-angleRotateZOffset));
const StelProjectorP prj = core->getProjection(transfo);
painter.setProjector(prj);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
painter.setColor(horizonPolygonLineColor[0], horizonPolygonL
ineColor[1], horizonPolygonLineColor[2], landFader.getInterstate());
painter.drawSphericalRegion(horizonPolygon.data(), StelPaint
er::SphericalPolygonDrawModeBoundary);
}
//else qDebug() << "no polygon defined";
}
const Vec4f color = StelApp::getInstance().getVisionModeNight() // Draw the horizon fog
? Vec4f(skyBrightness*nightBrightness, 0.0, 0.0, l void LandscapeOldStyle::drawFog(StelCore* core, StelPainter& sPainter) cons
andFader.getInterstate()) t
: Vec4f(skyBrightness, skyBrightness, skyBrightnes {
s, landFader.getInterstate()); if (!fogFader.getInterstate())
renderer->setGlobalColor(color); return;
// Lazily generate decoration sides. const float vpos = (tanMode||calibrated) ? radius*std::tan(fogAngleS
if(precomputedSides.empty()) hift*M_PI/180.) : radius*std::sin(fogAngleShift*M_PI/180.);
{ StelProjector::ModelViewTranformP transfo = core->getAltAzModelViewT
generatePrecomputedSides(renderer); ransform(StelCore::RefractionOff);
}
if (calibrated) // new in V0.13: take proper care of the fog layer.
This will work perfectly only for calibrated&&tanMode.
transfo->combine(Mat4d::zrotation(-(angleRotateZ+angleRotate
ZOffset)));
transfo->combine(Mat4d::translation(Vec3d(0.,0.,vpos)));
sPainter.setProjector(core->getProjection(transfo));
glBlendFunc(GL_ONE, GL_ONE);
sPainter.setColor(fogFader.getInterstate()*(0.1f+0.1f*landscapeBrigh
tness),
fogFader.getInterstate()*(0.1f+0.1f*landscapeBrigh
tness),
fogFader.getInterstate()*(0.1f+0.1f*landscapeBrigh
tness));
fogTex->bind();
const float height = (calibrated?
radius*(std::tan((fogAltAngle+fogAngleShift)
*M_PI/180.) - std::tan(fogAngleShift*M_PI/180.))
: ((tanMode) ? 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 side textures
void LandscapeOldStyle::drawDecor(StelCore* core, StelPainter& sPainter) co
nst
{
StelProjector::ModelViewTranformP transfo = core->getAltAzModelViewT
ransform(StelCore::RefractionOff);
transfo->combine(Mat4d::zrotation(-(angleRotateZ+angleRotateZOffset)
));
sPainter.setProjector(core->getProjection(transfo));
if (!landFader.getInterstate())
return;
sPainter.setColor(landscapeBrightness, landscapeBrightness, landscap
eBrightness, landFader.getInterstate());
// Draw decoration sides.
foreach (const LOSSide& side, precomputedSides) foreach (const LOSSide& side, precomputedSides)
{ {
side.tex->bind(); side.tex->bind();
renderer->drawVertexBuffer(side.vertices, side.indices, proj ector); sPainter.drawSphericalTriangles(side.arr, true, NULL, false) ;
} }
} }
void LandscapeOldStyle::generateGroundFanDisk(StelRenderer* renderer) // Draw the ground
void LandscapeOldStyle::drawGround(StelCore* core, StelPainter& sPainter) c
onst
{ {
// Precompute the vertex buffer for ground display if (!landFader.getInterstate())
// Make slicesPerSide = (3<<K) so that the return;
// innermost polygon of the fandisk becomes a triangle: const float vshift = radius * ((tanMode || calibrated) ? std::tan(gr
int slicesPerSide = std::max(1, 3 * 64 / (nbDecorRepeat * nbSide)); oundAngleShift) : std::sin(groundAngleShift));
StelProjector::ModelViewTranformP transfo = core->getAltAzModelViewT
ransform(StelCore::RefractionOff);
transfo->combine(Mat4d::zrotation(groundAngleRotateZ-angleRotateZOff
set) * Mat4d::translation(Vec3d(0,0,vshift)));
// draw a fan disk instead of a ordinary disk so that the inner slic sPainter.setProjector(core->getProjection(transfo));
es sPainter.setColor(landscapeBrightness, landscapeBrightness, landscap
// are not so slender. When they are too slender, culling errors occ eBrightness, landFader.getInterstate());
ur
// in cylinder projection mode. groundTex->bind();
int slicesInside = nbSide * slicesPerSide * nbDecorRepeat; sPainter.setArrays((Vec3d*)groundVertexArr.constData(), (Vec2f*)grou
int level = 0; ndTexCoordArr.constData());
while ((slicesInside & 1) == 0 && slicesInside > 4) sPainter.drawFromArray(StelPainter::Triangles, groundVertexArr.size(
)/3);
}
float LandscapeOldStyle::getOpacity(Vec3d azalt) const
{
if (angleRotateZOffset!=0.0f)
azalt.transfo4d(Mat4d::zrotation(angleRotateZOffset));
// in case we also have a horizon polygon defined, this is trivial a
nd fast.
if (horizonPolygon)
{ {
++level; if (horizonPolygon->contains(azalt) ) return 1.0f; else
slicesInside >>= 1; return 0.0f;
}
// Else, sample the images...
const float alt_rad = std::asin(azalt[2]); // sampled altitude, rad
ians
if (alt_rad < decorAngleShift*M_PI/180.0f) return 1.0f; // below dec
or, i.e. certainly opaque ground.
if (alt_rad > (decorAltAngle+decorAngleShift)*M_PI/180.0f) return 0.
0f; // above decor, i.e. certainly free sky.
if (!calibrated) // the result of this function has no real use here
: just complain and return result for math. horizon.
{
qDebug() << "Dubious result: Landscape \"" << name << "\" no
t calibrated. Result for mathematical horizon only.";
return (azalt[2] > 0 ? 0.0f : 1.0f);
}
float az=atan2(azalt[0], azalt[1]) / M_PI + 0.5f; // -0.5..+1.5
if (az<0) az+=2.0f; // 0..2 = N.E.S.
W.N
// we go to 0..1 domain, it's easier to think.
const float xShift=angleRotateZ /(2.0f*M_PI); // shift value in -1..
1
float az_phot=az*0.5f - 0.25f - xShift; // The 0.25 is caused b
y regular pano left edge being East. The xShift compensates any configured
angleRotateZ
az_phot=fmodf(az_phot, 1.0f);
if (az_phot<0) az_phot+=1.0f; // 0..
1 = image-X for a non-repeating pano photo
float az_panel = nbSide*nbDecorRepeat * az_phot; // azimuth in "pan
el space". Ex for nbS=4, nbDR=3: [0..[12, say 11.4
float x_in_panel=fmodf(az_panel, 1.0f);
int currentSide = (int) floor(fmodf(az_panel, nbSide)); // must beco
me 3
Q_ASSERT(currentSide<=nbSideTexs);
int x= (sides[currentSide].texCoords[0] + x_in_panel*(sides[currentS
ide].texCoords[2]-sides[currentSide].texCoords[0]))
* sidesImages[currentSide]->width(); // pixel X from
left.
// QImage has pixel 0/0 in top left corner. We must find image Y for
optionally cropped images.
// It should no longer be possible that sample position is outside c
ropped texture. in this case, assert(0) but again assume full transparency
and exit early.
float y_img_1; // y of the sampled altitude in 0..1 visible image he
ight from bottom
if (tanMode)
{
const float tanAlt=std::tan(alt_rad);
const float tanTop=std::tan((decorAltAngle+decorAngleShift)*
M_PI/180.0f);
const float tanBot=std::tan(decorAngleShift*M_PI/180.0f);
y_img_1=(tanAlt-tanBot)/(tanTop-tanBot); // Y position 0..1
in visible image height from bottom
} }
else
{ // adapted from spherical...
const float alt_pm1 = 2.0f * alt_rad / M_PI; // sampled al
titude, -1...+1 linear in altitude angle
const float img_top_pm1 = 1.0f-( (90.0f-decorAltAngle-decorA
ngleShift) / 90.0f); // the top line in -1..+1 (angular)
if (alt_pm1>img_top_pm1) { Q_ASSERT(0); return 0.0f; } // sh
ould have been caught above with alt_rad tests
const float img_bot_pm1 = 1.0f-((90.0f-decorAngleShift) / 90
.0f); // the bottom line in -1..+1 (angular)
if (alt_pm1<img_bot_pm1) { Q_ASSERT(0); return 1.0f; } // sh
ould have been caught above with alt_rad tests
groundFanDisk = renderer->createVertexBuffer<VertexP3T2>(PrimitiveTy y_img_1=(alt_pm1-img_bot_pm1)/(img_top_pm1-img_bot_pm1); //
pe_Triangles); the sampled altitude in 0..1 visible image height from bottom
groundFanDiskIndices = renderer->createIndexBuffer(IndexType_U16); }
StelGeometryBuilder()
.buildFanDisk(groundFanDisk, groundFanDiskIndices, radius, s // x0/y0 is lower left, x1/y1 upper right corner.
licesInside, level); float y_baseImg_1 = sides[currentSide].texCoords[1]+ y_img_1*(sides[
currentSide].texCoords[3]-sides[currentSide].texCoords[1]);
int y=(1.0-y_baseImg_1)*sidesImages[currentSide]->height();
// pixel Y from top.
QRgb pixVal=sidesImages[currentSide]->pixel(x, y);
// GZ: please leave the comment available for further development!
qDebug() << "Oldstyle Landscape sampling: az=" << az*180.0 << "° alt
=" << alt_rad*180.0f/M_PI
<< "°, xShift[-1..+1]=" << xShift << " az_phot[0..1
]=" << az_phot
<< " --> current side panel " << currentSide
<< ", w=" << sidesImages[currentSide]->width() << "
h=" << sidesImages[currentSide]->height()
<< " --> x:" << x << " y:" << y << " alpha:" << qAl
pha(pixVal)/255.0f;
return qAlpha(pixVal)/255.0f;
} }
// Draw the ground ///////////////////////////////////////////////////////////////////////////
void LandscapeOldStyle::drawGround(StelCore* core, StelRenderer* renderer) /////////////////////////
LandscapePolygonal::LandscapePolygonal(float _radius) : Landscape(_radius)
{}
LandscapePolygonal::~LandscapePolygonal()
{}
void LandscapePolygonal::load(const QSettings& landscapeIni, const QString&
landscapeId)
{
// loading the polygon has been moved to Landscape::loadCommon(), so
that all Landscape classes can use a polygon line.
loadCommon(landscapeIni, landscapeId);
QString type = landscapeIni.value("landscape/type").toString();
if(type != "polygonal")
{
qWarning() << "Landscape type mismatch for landscape "<< lan
dscapeId << ", expected polygonal, found " << type << ". No landscape in u
se.\n";
validLandscape = 0;
return;
}
if (horizonPolygon.isNull())
{
qWarning() << "Landscape " << landscapeId << " does not decl
are a valid polygonal_horizon_list. No landscape in use.\n";
validLandscape = 0;
return;
}
groundColor=StelUtils::strToVec3f( landscapeIni.value("landscape/gro
und_color", "0,0,0" ).toString() );
validLandscape = 1; // assume ok...
}
void LandscapePolygonal::draw(StelCore* core)
{ {
if (!landFader.getInterstate()) {return;} if(!validLandscape) return;
if(!landFader.getInterstate()) return;
const float vshift = (tanMode || calibrated) StelProjector::ModelViewTranformP transfo = core->getAltAzModelViewT
? radius * std::tan(groundAngleShift * M_PI / 180 ransform(StelCore::RefractionOff);
.0f) transfo->combine(Mat4d::zrotation(-angleRotateZOffset));
: radius * std::sin(groundAngleShift * M_PI / 180 const StelProjectorP prj = core->getProjection(transfo);
.0f); StelPainter sPainter(prj);
StelProjector::ModelViewTranformP transform =
core->getAltAzModelViewTransform(StelCore::RefractionOff);
transform->combine
(Mat4d::zrotation((groundAngleRotateZ - angleRotateZOffset)
* M_PI / 180.0f) *
Mat4d::translation(Vec3d(0.0, 0.0, vshift)));
StelProjectorP projector = core->getProjection(transform);
const Vec4f color = StelApp::getInstance().getVisionModeNight()
? Vec4f(skyBrightness*nightBrightness, 0.0, 0.0, l
andFader.getInterstate())
: Vec4f(skyBrightness, skyBrightness, skyBrightnes
s, landFader.getInterstate());
renderer->setGlobalColor(color);
groundTex->bind(); // Normal transparency mode for the transition blending
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
sPainter.setColor(landscapeBrightness*groundColor[0], landscapeBrigh
tness*groundColor[1], landscapeBrightness*groundColor[2], landFader.getInte
rstate());
sPainter.drawSphericalRegion(horizonPolygon.data(), StelPainter::Sph
ericalPolygonDrawModeFill);
// Lazily generate the ground fan disk. if (horizonPolygonLineColor[0] >= 0)
if(NULL == groundFanDisk)
{ {
Q_ASSERT_X(NULL == groundFanDiskIndices, Q_FUNC_INFO, sPainter.setColor(horizonPolygonLineColor[0], horizonPolygon
"Vertex buffer is NULL but index buffer is alread LineColor[1], horizonPolygonLineColor[2], landFader.getInterstate());
y generated"); sPainter.drawSphericalRegion(horizonPolygon.data(), StelPain
generateGroundFanDisk(renderer); ter::SphericalPolygonDrawModeBoundary);
} }
glDisable(GL_CULL_FACE);
}
// Draw the ground. float LandscapePolygonal::getOpacity(Vec3d azalt) const
renderer->drawVertexBuffer(groundFanDisk, groundFanDiskIndices, proj {
ector); if (angleRotateZOffset!=0.0f)
azalt.transfo4d(Mat4d::zrotation(angleRotateZOffset));
if (horizonPolygon->contains(azalt) ) return 1.0f; else return 0
.0f;
} }
///////////////////////////////////////////////////////////////////////////
/////////////
// LandscapeFisheye
//
LandscapeFisheye::LandscapeFisheye(float _radius) LandscapeFisheye::LandscapeFisheye(float _radius)
: Landscape(_radius) : Landscape(_radius)
, mapTex(NULL) , mapImage(NULL)
, fisheyeSphere(NULL) , texFov(360.)
{} {}
LandscapeFisheye::~LandscapeFisheye() LandscapeFisheye::~LandscapeFisheye()
{ {
if(NULL != fisheyeSphere) if (mapImage) delete mapImage;
{
delete fisheyeSphere;
}
if(NULL != mapTex)
{
delete mapTex;
}
} }
void LandscapeFisheye::load(const QSettings& landscapeIni, const QString& l andscapeId) void LandscapeFisheye::load(const QSettings& landscapeIni, const QString& l andscapeId)
{ {
loadCommon(landscapeIni, landscapeId); loadCommon(landscapeIni, landscapeId);
QString type = landscapeIni.value("landscape/type").toString(); QString type = landscapeIni.value("landscape/type").toString();
if(type != "fisheye") if(type != "fisheye")
{ {
qWarning() << "Landscape type mismatch for landscape "<< lan dscapeId << ", expected fisheye, found " << type << ". No landscape in use .\n"; qWarning() << "Landscape type mismatch for landscape "<< lan dscapeId << ", expected fisheye, found " << type << ". No landscape in use .\n";
validLandscape = 0; validLandscape = 0;
return; return;
} }
create(name, getTexturePath(landscapeIni.value("landscape/maptex").t create(name,
oString(), landscapeId), landscapeIni.value("landscape/texturefov", 360).toFloat(),
landscapeIni.value("landscape/texturefov", 360).toFloat(), getTexturePath(landscapeIni.value("landscape/maptex").toStrin
landscapeIni.value("landscape/angle_rotatez", 0.).toFloat()) g(), landscapeId),
; getTexturePath(landscapeIni.value("landscape/maptex_fog").toS
tring(), landscapeId),
getTexturePath(landscapeIni.value("landscape/maptex_illum").t
oString(), landscapeId),
landscapeIni.value("landscape/angle_rotatez", 0.f).toFloat())
;
} }
// create a fisheye landscape from basic parameters (no ini file needed) void LandscapeFisheye::create(const QString _name, float _texturefov, const
void LandscapeFisheye::create(const QString _name, const QString& _maptex, QString& _maptex, const QString &_maptexFog, const QString& _maptexIllum,
float atexturefov, float aangleRotateZ) const float _angleRotateZ)
{ {
// qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov; // qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov;
validLandscape = 1; // assume ok... validLandscape = 1; // assume ok...
name = _name; name = _name;
mapTexPath = _maptex; texFov = _texturefov*M_PI/180.f;
texFov = atexturefov*M_PI/180.f; angleRotateZ = _angleRotateZ*M_PI/180.f;
angleRotateZ = aangleRotateZ*M_PI/180.f; if (!horizonPolygon)
mapImage = new QImage(_maptex);
mapTex = StelApp::getInstance().getTextureManager().createTexture(_m
aptex, StelTexture::StelTextureParams(true));
if (_maptexIllum.length())
mapTexIllum = StelApp::getInstance().getTextureManager().cre
ateTexture(_maptexIllum, StelTexture::StelTextureParams(true));
if (_maptexFog.length())
mapTexFog = StelApp::getInstance().getTextureManager().creat
eTexture(_maptexFog, StelTexture::StelTextureParams(true));
const SphereParams params = SphereParams(radius).resolution(cols, ro
ws).orientInside();
fisheyeSphere = StelGeometryBuilder().buildSphereFisheye(params, tex
Fov);
} }
void LandscapeFisheye::draw(StelCore* core, StelRenderer* renderer) void LandscapeFisheye::draw(StelCore* core)
{ {
if(!validLandscape) return; if(!validLandscape) return;
if(!landFader.getInterstate()) return; if(!landFader.getInterstate()) return;
if(NULL == mapTex) StelProjector::ModelViewTranformP transfo = core->getAltAzModelViewT
ransform(StelCore::RefractionOff);
transfo->combine(Mat4d::zrotation(-(angleRotateZ+angleRotateZOffset)
));
const StelProjectorP prj = core->getProjection(transfo);
StelPainter sPainter(prj);
// Normal transparency mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
sPainter.setColor(landscapeBrightness, landscapeBrightness, landscap
eBrightness, landFader.getInterstate());
glEnable(GL_CULL_FACE);
sPainter.enableTexture2d(true);
glEnable(GL_BLEND);
mapTex->bind();
sPainter.sSphereMap(radius,cols,rows,texFov,1);
// NEW since 0.13: Fog also for fisheye...
if (mapTexFog)
{ {
mapTex = renderer->createTexture(mapTexPath, TextureParams() //glBlendFunc(GL_ONE, GL_ONE); // GZ: Take blending mode as
.generateMipmaps()); found in the old_style landscapes...
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); // GZ: better?
sPainter.setColor(fogFader.getInterstate()*(0.1f+0.1f*landsc
apeBrightness),
fogFader.getInterstate()*(0.1f+0.1f*landsc
apeBrightness),
fogFader.getInterstate()*(0.1f+0.1f*landsc
apeBrightness), fogFader.getInterstate());
mapTexFog->bind();
sPainter.sSphereMap(radius,cols,rows,texFov,1);
} }
StelProjector::ModelViewTranformP transform = core->getAltAzModelVie if (mapTexIllum && lightScapeBrightness>0.0f && core->getSkyDrawer()
wTransform(StelCore::RefractionOff); ->getFlagHasAtmosphere())
transform->combine(Mat4d::zrotation(-(angleRotateZ+(angleRotateZOffs {
et*M_PI/180.)))); glBlendFunc(GL_SRC_ALPHA, GL_ONE);
const StelProjectorP projector = core->getProjection(transform); sPainter.setColor(lightScapeBrightness, lightScapeBrightness
const Vec4f color = StelApp::getInstance().getVisionModeNight() , lightScapeBrightness, landFader.getInterstate());
? Vec4f(skyBrightness*nightBrightness, 0.0, 0.0, l mapTexIllum->bind();
andFader.getInterstate()) sPainter.sSphereMap(radius, cols, rows, texFov, 1);
: Vec4f(skyBrightness, skyBrightness, skyBrightnes }
s, landFader.getInterstate());
renderer->setGlobalColor(color);
renderer->setCulledFaces(CullFace_Back);
renderer->setBlendMode(BlendMode_Alpha);
mapTex->bind();
fisheyeSphere->draw(renderer, projector);
renderer->setCulledFaces(CullFace_None); glDisable(GL_CULL_FACE);
} }
float LandscapeFisheye::getOpacity(Vec3d azalt) const
{
if (angleRotateZOffset!=0.0f)
azalt.transfo4d(Mat4d::zrotation(angleRotateZOffset));
// in case we also have a horizon polygon defined, this is trivial a
nd fast.
if (horizonPolygon)
{
if (horizonPolygon->contains(azalt) ) return 1.0f; else
return 0.0f;
}
// Else, sample the image...
// QImage has pixel 0/0 in top left corner.
// The texture is taken from the center circle in the square texture
.
// It is possible that sample position is outside. in this case, ass
ume full opacity and exit early.
const float alt_rad = std::asin(azalt[2]); // sampled altitude, rad
ians
if (M_PI/2-alt_rad > texFov/2.0 ) return 1.0; // outside fov, in the
clamped texture zone: always opaque.
float radius=(M_PI/2-alt_rad)*2.0f/texFov; // radius in units of map
Image.height/2
float az=atan2(azalt[0], azalt[1]) + M_PI/2 - angleRotateZ; // -pi/2
..+3pi/2, real azimuth. NESW
// The texture map has south on top, east at right (if anglerotateZ
=0)
int x= mapImage->height()/2*(1 + radius*std::sin(az));
int y= mapImage->height()/2*(1 + radius*std::cos(az));
QRgb pixVal=mapImage->pixel(x, y);
// GZ: please leave the comment available for further development!
qDebug() << "Landscape sampling: az=" << (az+angleRotateZ)/M_PI*180.
0f << "° alt=" << alt_rad/M_PI*180.f
<< "°, w=" << mapImage->width() << " h=" << mapImag
e->height()
<< " --> x:" << x << " y:" << y << " alpha:" << qAl
pha(pixVal)/255.0f;
return qAlpha(pixVal)/255.0f;
}
///////////////////////////////////////////////////////////////////////////
//////////////////////
// spherical panoramas // spherical panoramas
LandscapeSpherical::LandscapeSpherical(float _radius) LandscapeSpherical::LandscapeSpherical(float _radius)
: Landscape(_radius) : Landscape(_radius)
, mapTex(NULL) , mapTexTop(0.)
{ , mapTexBottom(0.)
} , fogTexTop(0.)
, fogTexBottom(0.)
, illumTexTop(0.)
, illumTexBottom(0.)
, mapImage(NULL)
{}
LandscapeSpherical::~LandscapeSpherical() LandscapeSpherical::~LandscapeSpherical()
{ {
if(NULL != landscapeSphere) if (mapImage) delete mapImage;
{
delete landscapeSphere;
landscapeSphere = NULL;
}
if(NULL != mapTex)
{
delete mapTex;
}
} }
void LandscapeSpherical::load(const QSettings& landscapeIni, const QString& landscapeId) void LandscapeSpherical::load(const QSettings& landscapeIni, const QString& landscapeId)
{ {
loadCommon(landscapeIni, landscapeId); loadCommon(landscapeIni, landscapeId);
// if (horizonPolygon)
// qDebug() << "This landscape, " << landscapeId << ", has a po
lygon defined!" ;
// else
// qDebug() << "This landscape, " << landscapeId << ", has no p
olygon defined!" ;
QString type = landscapeIni.value("landscape/type").toString(); QString type = landscapeIni.value("landscape/type").toString();
if (type != "spherical") if (type != "spherical")
{ {
qWarning() << "Landscape type mismatch for landscape "<< lan dscapeId qWarning() << "Landscape type mismatch for landscape "<< lan dscapeId
<< ", expected spherical, found " << type << ", expected spherical, found " << type
<< ". No landscape in use.\n"; << ". No landscape in use.\n";
validLandscape = 0; validLandscape = 0;
return; return;
} }
create(name, getTexturePath(landscapeIni.value("landscape/maptex").t create(name,
oString(), landscapeId), getTexturePath(landscapeIni.value("landscape/maptex").toStrin
landscapeIni.value("landscape/angle_rotatez", 0.f).toFloat() g(), landscapeId),
); getTexturePath(landscapeIni.value("landscape/maptex_fog").toS
} tring(), landscapeId),
getTexturePath(landscapeIni.value("landscape/maptex_illum").t
// create a spherical landscape from basic parameters (no ini file needed) oString(), landscapeId),
void LandscapeSpherical::create(const QString _name, const QString& _maptex landscapeIni.value("landscape/angle_rotatez" , 0.f).to
, float _angleRotateZ) Float(),
landscapeIni.value("landscape/maptex_top" , 90.f).to
Float(),
landscapeIni.value("landscape/maptex_bottom" , -90.f).to
Float(),
landscapeIni.value("landscape/maptex_fog_top" , 90.f).to
Float(),
landscapeIni.value("landscape/maptex_fog_bottom" , -90.f).to
Float(),
landscapeIni.value("landscape/maptex_illum_top" , 90.f).to
Float(),
landscapeIni.value("landscape/maptex_illum_bottom", -90.f).to
Float());
}
//// create a spherical landscape from basic parameters (no ini file needed
)
void LandscapeSpherical::create(const QString _name, const QString& _maptex
, const QString& _maptexFog, const QString& _maptexIllum, const float _angl
eRotateZ,
const float
_mapTexTop, const float _mapTexBottom,
const float
_fogTexTop, const float _fogTexBottom,
const float
_illumTexTop, const float _illumTexBottom)
{ {
// qDebug() << _name << " " << _fullpath << " " << _maptex << " " << _texturefov; //qDebug() << "LandscapeSpherical::create():"<< _name << " : " << _m aptex << " : " << _maptexFog << " : " << _maptexIllum << " : " << _angleRot ateZ;
validLandscape = 1; // assume ok... validLandscape = 1; // assume ok...
name = _name; name = _name;
angleRotateZ = _angleRotateZ*M_PI/180.f; angleRotateZ = _angleRotateZ *M_PI/180.f; // Defined in ini
mapTexPath = _maptex; --> internal prg value
mapTexTop = (90.f-_mapTexTop) *M_PI/180.f; // top 90
const SphereParams params --> 0
= SphereParams(radius).resolution(64, 48).orientInside().fli mapTexBottom = (90.f-_mapTexBottom) *M_PI/180.f; // bottom -90
pTexture(); --> pi
landscapeSphere = StelGeometryBuilder().buildSphereUnlit(params); fogTexTop = (90.f-_fogTexTop) *M_PI/180.f;
fogTexBottom = (90.f-_fogTexBottom) *M_PI/180.f;
illumTexTop = (90.f-_illumTexTop) *M_PI/180.f;
illumTexBottom= (90.f-_illumTexBottom)*M_PI/180.f;
if (!horizonPolygon)
mapImage = new QImage(_maptex);
mapTex = StelApp::getInstance().getTextureManager().createTexture(_m
aptex, StelTexture::StelTextureParams(true));
if (_maptexIllum.length())
mapTexIllum = StelApp::getInstance().getTextureManager().cre
ateTexture(_maptexIllum, StelTexture::StelTextureParams(true));
if (_maptexFog.length())
mapTexFog = StelApp::getInstance().getTextureManager().creat
eTexture(_maptexFog, StelTexture::StelTextureParams(true));
} }
void LandscapeSpherical::draw(StelCore* core, StelRenderer* renderer) void LandscapeSpherical::draw(StelCore* core)
{ {
if(!validLandscape) return; if(!validLandscape) return;
if(!landFader.getInterstate()) return; if(!landFader.getInterstate()) return;
if(NULL == mapTex)
StelProjector::ModelViewTranformP transfo = core->getAltAzModelViewT
ransform(StelCore::RefractionOff);
transfo->combine(Mat4d::zrotation(-(angleRotateZ+angleRotateZOffset)
));
const StelProjectorP prj = core->getProjection(transfo);
StelPainter sPainter(prj);
// Normal transparency mode
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
sPainter.setColor(landscapeBrightness, landscapeBrightness, landscap
eBrightness, landFader.getInterstate());
glEnable(GL_CULL_FACE);
sPainter.enableTexture2d(true);
glEnable(GL_BLEND);
mapTex->bind();
// TODO: verify that this works correctly for custom projections [co
mment not by GZ]
// seam is at East, except if angleRotateZ has been given.
sPainter.sSphere(radius, 1.0, cols, rows, 1, true, mapTexTop, mapTex
Bottom);
// Since 0.13: Fog also for sphericals...
if (mapTexFog)
{
//glBlendFunc(GL_ONE, GL_ONE); // GZ: blending mode as found
in the old_style landscapes...
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR); // GZ: better?
sPainter.setColor(fogFader.getInterstate()*(0.1f+0.1f*landsc
apeBrightness),
fogFader.getInterstate()*(
0.1f+0.1f*landscapeBrightness),
fogFader.getInterstate()*(
0.1f+0.1f*landscapeBrightness), fogFader.getInterstate());
mapTexFog->bind();
sPainter.sSphere(radius, 1.0, cols, (int) ceil(rows*(fogTexT
op-fogTexBottom)/(mapTexTop-mapTexBottom)), 1, true, fogTexTop, fogTexBotto
m);
}
// Self-luminous layer (Light pollution etc). This looks striking!
if (mapTexIllum && lightScapeBrightness>0.0f && core->getSkyDrawer()
->getFlagHasAtmosphere())
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
sPainter.setColor(lightScapeBrightness, lightScapeBrightness
, lightScapeBrightness, landFader.getInterstate());
mapTexIllum->bind();
sPainter.sSphere(radius, 1.0, cols, (int) ceil(rows*(illumTe
xTop-illumTexBottom)/(mapTexTop-mapTexBottom)), 1, true, illumTexTop, illum
TexBottom);
}
//qDebug() << "before drawing line";
// If a horizon line also has been defined, draw it.
if (horizonPolygon && (horizonPolygonLineColor[0] >= 0))
{ {
mapTex = renderer->createTexture(mapTexPath, TextureParams() //qDebug() << "drawing line";
.generateMipmaps()); transfo = core->getAltAzModelViewTransform(StelCore::Refract
ionOff);
transfo->combine(Mat4d::zrotation(-angleRotateZOffset));
const StelProjectorP prj = core->getProjection(transfo);
sPainter.setProjector(prj);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
sPainter.setColor(horizonPolygonLineColor[0], horizonPolygon
LineColor[1], horizonPolygonLineColor[2], landFader.getInterstate());
sPainter.drawSphericalRegion(horizonPolygon.data(), StelPain
ter::SphericalPolygonDrawModeBoundary);
} }
//else qDebug() << "no polygon defined";
glDisable(GL_CULL_FACE);
StelProjector::ModelViewTranformP transform = core->getAltAzModelVie }
wTransform(StelCore::RefractionOff);
transform->combine(Mat4d::zrotation(-(angleRotateZ+(angleRotateZOffs
et*M_PI/180.))));
const StelProjectorP projector = core->getProjection(transform);
renderer->setBlendMode(BlendMode_Alpha); //! Sample landscape texture for transparency. May be used for advanced vis
ibility computation like sunrise on the visible horizon etc.
//! @param azalt: normalized direction in alt-az frame
//! @retval alpha (0..1), where 0=fully transparent.
float LandscapeSpherical::getOpacity(Vec3d azalt) const
{
if (angleRotateZOffset!=0.0f)
azalt.transfo4d(Mat4d::zrotation(angleRotateZOffset));
const Vec4f color = StelApp::getInstance().getVisionModeNight() // in case we also have a horizon polygon defined, this is trivial a
? Vec4f(skyBrightness*nightBrightness, 0.0, 0.0, l nd fast.
andFader.getInterstate()) if (horizonPolygon)
: Vec4f(skyBrightness, skyBrightness, skyBrightnes {
s, landFader.getInterstate()); if (horizonPolygon->contains(azalt) ) return 1.0f; else
renderer->setGlobalColor(color); return 0.0f;
renderer->setCulledFaces(CullFace_Back); }
mapTex->bind(); // Else, sample the image...
// QImage has pixel 0/0 in top left corner. We must first find image
Y for optionally cropped images.
// It is possible that sample position is outside cropped texture. i
n this case, assume full transparency and exit early.
const float alt_pm1 = 2.0f * std::asin(azalt[2]) / M_PI; // sample
d altitude, -1...+1 linear in altitude angle
const float img_top_pm1 = 1.0f-2.0f*(mapTexTop / M_PI); // the to
p line in -1..+1
if (alt_pm1>img_top_pm1) return 0.0f;
const float img_bot_pm1 = 1.0f-2.0f*(mapTexBottom / M_PI); // the bo
ttom line in -1..+1
if (alt_pm1<img_bot_pm1) return 1.0f; // rare case of a hole in the
ground. Even though there is a visible hole, play opaque.
float y_img_1=(alt_pm1-img_bot_pm1)/(img_top_pm1-img_bot_pm1); // th
e sampled altitude in 0..1 image height from bottom
int y=(1.0-y_img_1)*mapImage->height(); // pixel Y from to
p.
float az=atan2(azalt[0], azalt[1]) / M_PI + 0.5f; // -0.5..+1.5
if (az<0) az+=2.0f; // 0..2 = N.E.S.
W.N
const float xShift=(angleRotateZ) /M_PI; // shift value in -2..2
float az_phot=az - 0.5f - xShift; // The 0.5 is caused by regul
ar pano left edge being East. The xShift compensates any configured angleRo
tateZ
az_phot=fmodf(az_phot, 2.0f);
if (az_phot<0) az_phot+=2.0f; // 0..
2 = image-X
int x=(az_phot/2.0f) * mapImage->width(); // pixel X from left.
QRgb pixVal=mapImage->pixel(x, y);
// GZ: please leave the comment available for further development!
qDebug() << "Landscape sampling: az=" << az*180.0 << "° alt=" << alt
_pm1*90.0f
<< "°, xShift[-2..+2]=" << xShift << " az_phot[0..2
]=" << az_phot
<< ", w=" << mapImage->width() << " h=" << mapImage
->height()
<< " --> x:" << x << " y:" << y << " alpha:" << qAl
pha(pixVal)/255.0f;
return qAlpha(pixVal)/255.0f;
landscapeSphere->draw(renderer, projector);
// TODO: verify that this works correctly for custom projections
// seam is at East
// GZ: Want better angle resolution, optional!
renderer->setCulledFaces(CullFace_None);
} }
 End of changes. 108 change blocks. 
545 lines changed or deleted 998 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/