Meteor.cpp   Meteor.cpp 
/* /*
* Stellarium * Stellarium
* This file Copyright (C) 2004 Robert Spearman * Copyright (C) 2004 Robert Spearman
* Copyright (C) 2014 Marcos Cardinot
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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.
*/ */
// This is an ad hoc meteor model
// Could use a simple ablation physics model in the future
/*
NOTE: Here the radiant is always along the ecliptic at the apex of the Eart
h's way.
In reality, individual meteor streams have varying velocity vectors and the
refore radiants
which are generally not at the apex of the Earth's way, such as the Perseid
s shower.
*/
// Improved realism and efficiency 2004-12
#include <cstdlib>
#include "Meteor.hpp" #include "Meteor.hpp"
#include "StelCore.hpp" #include "StelCore.hpp"
#include "renderer/StelRenderer.hpp"
#include "StelToneReproducer.hpp"
#include "StelMovementMgr.hpp" #include "StelMovementMgr.hpp"
#include "StelPainter.hpp"
#include "StelTexture.hpp"
Meteor::Meteor(const StelCore* core, double v) StelTextureSP Meteor::bolideTexture;
: vertexBuffer(NULL)
{
const StelToneReproducer* eye = core->getToneReproducer();
// velocity = 11+(double)rand()/((double)RAND_MAX+1)*v; // abs ran Meteor::Meteor(const StelCore* core, float v)
ge 11-72 km/s : m_distMultiplier(0.)
velocity=v; , m_segments(10)
{
maxMag = 1; // determine meteor velocity
// abs range 11-72 km/s by default (see line 427 in StelApp.cpp)
m_speed = 11+(float)rand()/((float)RAND_MAX+1)*(v-11);
// view matrix of sporadic meteors model
float alpha = (double)rand()/((double)RAND_MAX+1)*2*M_PI;
float delta = M_PI_2 - (double)rand()/((double)RAND_MAX+1)*M_PI;
m_viewMatrix = Mat4d::zrotation(alpha) * Mat4d::yrotation(delta);
// building meteor model
m_alive = initMeteorModel(core, m_segments, m_viewMatrix, meteor);
if (!m_alive)
{
return;
}
// determine meteor model view matrix (want z in dir of travel of ea // building lineColorArray and trainColorArray
rth, z=0 at center of earth) buildColorArrays(m_segments, getRandColor(), m_lineColorArray, m_tra
// meteor life is so short, no need to recalculate inColorArray);
double equ_rotation; // rotation needed to align with path of earth }
Vec3d sun_dir = core->heliocentricEclipticToEquinoxEqu( Vec3d(0,0,0)
);
Mat4d tmat = Mat4d::xrotation(-23.45f*M_PI/180.f); // ecliptical ti Meteor::~Meteor()
lt {
sun_dir.transfo4d(tmat); // convert to ecliptical coordinates }
sun_dir.normalize();
equ_rotation = acos( sun_dir.dot( Vec3d(1,0,0) ) );
if( sun_dir[1] < 0 ) equ_rotation = 2*M_PI - equ_rotation;
equ_rotation -= M_PI_2; // returns true if alive
bool Meteor::initMeteorModel(const StelCore* core, const int segments, cons
t Mat4d viewMatrix, MeteorModel &mm)
{
float high_range = EARTH_RADIUS + HIGH_ALTITUDE;
float low_range = EARTH_RADIUS + LOW_ALTITUDE;
mmat = Mat4d::xrotation(23.45f*M_PI/180.f) * Mat4d::zrotation(equ_ro // find observer position in meteor coordinate system
tation) * Mat4d::yrotation(M_PI_2); mm.obs = core->altAzToJ2000(Vec3d(0,0,EARTH_RADIUS));
mm.obs.transfo4d(viewMatrix.transpose());
// select random trajectory using polar coordinates in XY plane, cen tered on observer // select random trajectory using polar coordinates in XY plane, cen tered on observer
xydistance = (double)rand()/((double)RAND_MAX+1)*(VISIBLE_RADIUS); mm.xydistance = (double)rand() / ((double)RAND_MAX+1)*(VISIBLE_RADIU
double angle = (double)rand()/((double)RAND_MAX+1)*2*M_PI; S);
float angle = (double)rand() / ((double)RAND_MAX+1)*2*M_PI;
// find observer position in meteor coordinate system
obs = core->altAzToEquinoxEqu(Vec3d(0,0,EARTH_RADIUS));
obs.transfo4d(mmat.transpose());
// set meteor start x,y // set meteor start x,y
posInternal[0] = posTrain[0] = position[0] = xydistance*cos(angle) + mm.position[0] = mm.posTrain[0] = mm.xydistance*cos(angle) + mm.obs[
obs[0]; 0];
posInternal[1] = posTrain[1] = position[1] = xydistance*sin(angle) + mm.position[1] = mm.posTrain[1] = mm.xydistance*sin(angle) + mm.obs[
obs[1]; 1];
// determine life of meteor (start and end z value based on atmosphe
re burn altitudes)
// D is distance from center of earth // D is distance from center of earth
double D = sqrt( position[0]*position[0] + position[1]*position[1] ) ; float D = sqrt(mm.position[0]*mm.position[0] + mm.position[1]*mm.pos ition[1]);
if( D > EARTH_RADIUS+HIGH_ALTITUDE ) if (D > high_range) // won't be visible, meteor still dead
{ {
// won't be visible return false;
alive = 0;
return;
} }
startH = sqrt( pow(EARTH_RADIUS+HIGH_ALTITUDE,2) - D*D); mm.posTrain[2] = mm.position[2] = mm.startH = sqrt(high_range*high_r ange - D*D);
// determine end of burn point, and nearest point to observer for di stance mag calculation // determine end of burn point, and nearest point to observer for di stance mag calculation
// mag should be max at nearest point still burning // mag should be max at nearest point still burning
if( D > EARTH_RADIUS+LOW_ALTITUDE ) if (D > low_range)
{ {
endH = -startH; // earth grazing mm.endH = -mm.startH; // earth grazing
minDist = xydistance; mm.minDist = mm.xydistance;
} }
else else
{ {
endH = sqrt( pow(EARTH_RADIUS+LOW_ALTITUDE,2) - D*D); mm.endH = sqrt(low_range*low_range - D*D);
minDist = sqrt( xydistance*xydistance + pow( endH - obs[2], mm.minDist = sqrt(mm.xydistance*mm.xydistance + pow(mm.endH
2) ); - mm.obs[2], 2));
} }
<span class="insert">mm.obs[2], 2));</span>
if(minDist > VISIBLE_RADIUS ) if (mm.minDist > VISIBLE_RADIUS)
{ {
// on average, not visible (although if were zoomed ...) // on average, not visible (although if were zoomed ...)
alive = 0; return false; //meteor still dead
return;
}
/* experiment
// limit lifetime to 0.5-3.0 sec
double tmp_h = startH - velocity * (0.5 + (double)rand()/((double)RA
ND_MAX+1) * 2.5);
if( tmp_h > endH ) {
endH = tmp_h;
} }
*/
posTrain[2] = position[2] = startH;
// qDebug("New meteor: %f %f s:%f e:%f v:%f\n", position[0], positi
on[1], startH, endH, velocity);
alive = 1; // determine intensity [-3; 4.5]
train = 1; float Mag1 = (double)rand()/((double)RAND_MAX+1)*7.5f - 3;
float Mag2 = (double)rand()/((double)RAND_MAX+1)*7.5f - 3;
// Determine drawing color given magnitude and eye
// (won't be visible during daylight)
// *** color varies somewhat based on velocity, plus atmosphere redd
ening
// determine intensity
float Mag1 = (double)rand()/((double)RAND_MAX+1)*6.75f - 3;
float Mag2 = (double)rand()/((double)RAND_MAX+1)*6.75f - 3;
float Mag = (Mag1 + Mag2)/2.0f; float Mag = (Mag1 + Mag2)/2.0f;
mag = (5. + Mag) / 256.0; // compute RMag and CMag
if (mag>250) mag = mag - 256; RCMag rcMag;
core->getSkyDrawer()->computeRCMag(Mag, &rcMag);
mm.mag = rcMag.luminance;
float term1 = std::exp(-0.92103f*(mag + 12.12331f)) * 108064.73f; // most visible meteors are under about 180km distant
// scale max mag down if outside this range
float scale = 1;
if (mm.minDist)
{
scale = 180*180 / (mm.minDist*mm.minDist);
}
if (scale < 1)
{
mm.mag *= scale;
}
float cmag=1.f; mm.firstBrightSegment = (double)rand()/((double)RAND_MAX+1)*segments
float rmag; ;
// Compute the equivalent star luminance for a 5 arc min circle and // If everything is ok until here,
convert it return true; //the meteor is alive
// in function of the eye adaptation }
rmag = eye->adaptLuminanceScaled(term1);
rmag = rmag/powf(core->getMovementMgr()->getCurrentFov(),0.85f)*500.
f;
// if size of star is too small (blink) we put its size to 1.2 --> n Vec4f Meteor::getColorFromName(QString colorName) {
o more blink int R, G, B; // 0-255
// And we compensate the difference of brighteness with cmag if (colorName == "violet") { // Calcium
if (rmag<1.2f) R = 176;
{ G = 67;
cmag=rmag*rmag/1.44f; B = 172;
} else if (colorName == "blueGreen") { // Magnesium
R = 0;
G = 255;
B = 152;
} else if (colorName == "yellow") { // Iron
R = 255;
G = 255;
B = 0;
} else if (colorName == "orangeYellow") { // Sodium
R = 255;
G = 160;
B = 0;
} else if (colorName == "red") { // atmospheric nitrogen and oxygen
R = 255;
G = 30;
B = 0;
} else { // white
R = 255;
G = 255;
B = 255;
} }
mag = cmag; // assumes white return Vec4f(R/255.f, G/255.f, B/255.f, 1);
}
// most visible meteors are under about 180km distant QList<Meteor::colorPair> Meteor::getRandColor() {
// scale max mag down if outside this range QList<colorPair> colors;
float scale = 1; float prob = (double)rand()/((double)RAND_MAX+1);
if(minDist!=0) scale = 180*180/(minDist*minDist); if (prob > 0.9) {
if( scale < 1 ) mag *= scale; colors.push_back(Meteor::colorPair("white", 70));
colors.push_back(Meteor::colorPair("orangeYellow", 10));
colors.push_back(Meteor::colorPair("yellow", 10));
colors.push_back(Meteor::colorPair("blueGreen", 10));
} else if (prob > 0.85) {
colors.push_back(Meteor::colorPair("white", 80));
colors.push_back(Meteor::colorPair("violet", 20));
} else if (prob > 0.80) {
colors.push_back(Meteor::colorPair("white", 80));
colors.push_back(Meteor::colorPair("orangeYellow", 20));
} else {
colors.push_back(Meteor::colorPair("white", 100));
}
return colors;
} }
Meteor::~Meteor() void Meteor::buildColorArrays(const int segments,
const QList<colorPair> colors,
QList<Vec4f> &lineColorArray,
QList<Vec4f> &trainColorArray)
{ {
if(NULL != vertexBuffer) // building color arrays (line and prism)
{ int totalOfSegments = 0;
delete vertexBuffer; int currentSegment = 1+(double)rand()/((double)RAND_MAX+1)*(segments
-1);
for (int colorIndex=0; colorIndex<colors.size(); colorIndex++) {
colorPair currentColor = colors[colorIndex];
// segments which we'll paint with the current color
int numOfSegments = segments*(currentColor.second / 100.f) +
0.4f; // +0.4 affect approximation
if (colorIndex == colors.size()-1) {
numOfSegments = segments - totalOfSegments;
}
totalOfSegments += numOfSegments;
for (int i=0; i<numOfSegments; i++) {
lineColorArray.insert(currentSegment, getColorFromNa
me(currentColor.first));
trainColorArray.insert(currentSegment, getColorFromN
ame(currentColor.first));
if (currentSegment >= segments-1) {
currentSegment = 0;
} else {
currentSegment++;
}
trainColorArray.insert(currentSegment, getColorFromN
ame(currentColor.first));
}
} }
} }
// returns true if alive bool Meteor::updateMeteorModel(double deltaTime, double speed, MeteorModel
bool Meteor::update(double deltaTime) &mm)
{ {
if(!alive) return(0); if (mm.position[2] < mm.endH)
if(position[2] < endH)
{ {
// burning has stopped so magnitude fades out // burning has stopped so magnitude fades out
// assume linear fade out // assume linear fade out
mm.mag -= deltaTime/500.0f;
mag -= maxMag * deltaTime/500.0f; if(mm.mag < 0)
if(mag < 0) alive=0; // no longer visible {
return false; // no longer visible
}
} }
// *** would need time direction multiplier to allow reverse time re play // *** would need time direction multiplier to allow reverse time re play
position[2] = position[2] - velocity*deltaTime/1000.0f; float dt = 820+(double)rand()/((double)RAND_MAX+1)*185; // range 820
-1005
mm.position[2] -= speed*deltaTime/dt;
// train doesn't extend beyond start of burn // train doesn't extend beyond start of burn
if(position[2] + velocity*0.5f > startH) if (mm.position[2] + speed*0.5f > mm.startH)
{ {
posTrain[2] = startH ; mm.posTrain[2] = mm.startH;
} }
else else
{ {
posTrain[2] -= velocity*deltaTime/1000.0f; mm.posTrain[2] -= speed*deltaTime/dt;
} }
//qDebug("meteor position: %f delta_t %d\n", position[2], deltaTime) return true;
; }
// determine visual magnitude based on distance to observer // returns true if alive
double dist = sqrt(xydistance*xydistance + pow( position[2]-obs[2], bool Meteor::update(double deltaTime)
2)); {
if (!m_alive)
{
return false;
}
if(dist == 0) dist = .01; // just to be cautious (meteor hits obser ver!) m_alive = updateMeteorModel(deltaTime, m_speed, meteor);
distMultiplier = minDist*minDist / (dist*dist); // determine visual magnitude based on distance to observer
double dist = sqrt(meteor.xydistance*meteor.xydistance + pow(meteor.
position[2]-meteor.obs[2], 2));
if (dist == 0)
{
dist = .01; // just to be cautious (meteor hits observer!
)
}
m_distMultiplier = meteor.minDist*meteor.minDist / (dist*dist);
return(alive); return m_alive;
} }
// Assumes that we are in local frame void Meteor::insertVertex(const StelCore* core, const Mat4d& viewMatrix, QV
void Meteor::draw(const StelCore* core, StelProjectorP projector, StelRende ector<Vec3d> &vertexArray, Vec3d vertex)
rer* renderer)
{ {
if (!alive) {return;} vertex.transfo4d(viewMatrix);
vertex = core->j2000ToAltAz(vertex);
vertex[2] -= EARTH_RADIUS;
vertex/=1216; // 1216 is to scale down under 1 for desktop version
vertexArray.push_back(vertex);
}
Vec3d spos = position; void Meteor::calculateThickness(const StelCore* core, float& thickness, flo
Vec3d epos = posTrain; at& bolideSize)
{
float maxFOV = core->getMovementMgr()->getMaxFov();
float FOV = core->getMovementMgr()->getCurrentFov();
thickness = 2*log(FOV + 0.25)/(1.2*maxFOV - (FOV + 0.25)) + 0.01;
if (FOV <= 0.5)
{
thickness = 0.013 * FOV; // decreasing faster
} else if (FOV > 100.0) {
thickness = 0; // remove prism
}
bolideSize = thickness*3;
}
// convert to equ void Meteor::drawBolide(const StelCore* core, StelPainter& sPainter, const
spos.transfo4d(mmat); MeteorModel& mm,
epos.transfo4d(mmat); const Mat4d& viewMatrix, const float bolideSize)
{
// convert to local and correct for earth radius [since equ and loca if (!bolideSize) {
l coordinates in stellarium use same 0 point!] return;
spos = core->equinoxEquToAltAz(spos);
epos = core->equinoxEquToAltAz(epos);
spos[2] -= EARTH_RADIUS;
epos[2] -= EARTH_RADIUS;
// 1216 is to scale down under 1 for desktop version
spos /= 1216;
epos /= 1216;
// qDebug("[%f %f %f] (%d, %d) (%d, %d)\n", position[0], position[1
], position[2], (int)start[0], (int)start[1], (int)end[0], (int)end[1]);
if(NULL == vertexBuffer)
{
// This well need to be rewritten if the train data member g
ets changed after construction.
vertexBuffer =
renderer->createVertexBuffer<Vertex>(train ? Primiti
veType_LineStrip : PrimitiveType_Points);
}
if (train)
{
// connect this point with last drawn point
const double tmag = mag * distMultiplier;
// compute an intermediate point so can curve slightly along
projection distortions
Vec3d posi = posInternal;
posi[2] = position[2] + (posTrain[2] - position[2]) / 2;
posi.transfo4d(mmat);
posi = core->equinoxEquToAltAz( posi );
posi[2] -= EARTH_RADIUS;
posi /= 1216;
vertexBuffer->unlock();
vertexBuffer->clear();
vertexBuffer->addVertex(Vertex(epos, Vec4f(0.0f, 0.0f, 0.0f,
0.0f)));
vertexBuffer->addVertex(Vertex(posi, Vec4f(1.0f, 1.0f, 1.0f,
tmag * 0.5)));
vertexBuffer->addVertex(Vertex(spos, Vec4f(1.0f, 1.0f, 1.0f,
tmag)));
vertexBuffer->lock();
renderer->drawVertexBuffer(vertexBuffer, NULL, projector);
} }
else
// bolide
//
QVector<Vec3d> vertexArrayBolide;
QVector<Vec4f> colorArrayBolide;
Vec4f bolideColor = Vec4f(1,1,1,mm.mag);
Vec3d topLeft = mm.position;
topLeft[1] -= bolideSize;
insertVertex(core, viewMatrix, vertexArrayBolide, topLeft);
colorArrayBolide.push_back(bolideColor);
Vec3d topRight = mm.position;
topRight[0] -= bolideSize;
insertVertex(core, viewMatrix, vertexArrayBolide, topRight);
colorArrayBolide.push_back(bolideColor);
Vec3d bottomRight = mm.position;
bottomRight[1] += bolideSize;
insertVertex(core, viewMatrix, vertexArrayBolide, bottomRight);
colorArrayBolide.push_back(bolideColor);
Vec3d bottomLeft = mm.position;
bottomLeft[0] += bolideSize;
insertVertex(core, viewMatrix, vertexArrayBolide, bottomLeft);
colorArrayBolide.push_back(bolideColor);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
sPainter.enableClientStates(true, true, true);
Meteor::bolideTexture->bind();
static const float texCoordData[] = {1.,0., 0.,0., 0.,1., 1.,1.};
sPainter.setTexCoordPointer(2, GL_FLOAT, texCoordData);
sPainter.setColorPointer(4, GL_FLOAT, colorArrayBolide.constData());
sPainter.setVertexPointer(3, GL_DOUBLE, vertexArrayBolide.constData(
));
sPainter.drawFromArray(StelPainter::TriangleFan, vertexArrayBolide.s
ize(), 0, true);
glDisable(GL_BLEND);
sPainter.enableClientStates(false);
}
void Meteor::drawTrain(const StelCore *core, StelPainter& sPainter, const M
eteorModel& mm,
const Mat4d& viewMatrix, const float thickness, const
int segments,
QList<Vec4f> lineColorArray, QList<Vec4f> trainColorA
rray)
{
if (segments != lineColorArray.size() || 2*segments != trainColorArr
ay.size())
{ {
vertexBuffer->unlock(); qWarning() << "Meteor: color arrays have an inconsistent siz
vertexBuffer->clear(); e!";
// A line with two identical points ends up being a point. return;
vertexBuffer->addVertex(Vertex(spos, Vec4f(1.0f, 1.0f, 1.0f,
1.0f)));
vertexBuffer->lock();
renderer->drawVertexBuffer(vertexBuffer, NULL, projector);
} }
// train (triangular prism)
//
QVector<Vec3d> vertexArrayLine;
QVector<Vec3d> vertexArrayL;
QVector<Vec3d> vertexArrayR;
QVector<Vec3d> vertexArrayTop;
Vec3d posTrainB = mm.posTrain;
posTrainB[0] += thickness*0.7;
posTrainB[1] += thickness*0.7;
Vec3d posTrainL = mm.posTrain;
posTrainL[1] -= thickness;
Vec3d posTrainR = mm.posTrain;
posTrainR[0] -= thickness;
for (int i=0; i<segments; i++) {
float mag = mm.mag * i/(3* (segments-1));
if (i > mm.firstBrightSegment) {
mag *= 12/5;
}
double height = mm.posTrain[2] + i*(mm.position[2] - mm.posT
rain[2])/(segments-1);
Vec3d posi;
posi = mm.posTrain;
posi[2] = height;
insertVertex(core, viewMatrix, vertexArrayLine, posi);
posi = posTrainB;
posi[2] = height;
insertVertex(core, viewMatrix, vertexArrayL, posi);
insertVertex(core, viewMatrix, vertexArrayR, posi);
posi = posTrainL;
posi[2] = height;
insertVertex(core, viewMatrix, vertexArrayL, posi);
insertVertex(core, viewMatrix, vertexArrayTop, posi);
posi = posTrainR;
posi[2] = height;
insertVertex(core, viewMatrix, vertexArrayR, posi);
insertVertex(core, viewMatrix, vertexArrayTop, posi);
lineColorArray[i][3] = mag;
trainColorArray[i*2][3] = mag;
trainColorArray[i*2+1][3] = mag;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
sPainter.enableClientStates(true, false, true);
if (thickness) {
sPainter.setColorPointer(4, GL_FLOAT, trainColorArray.toVect
or().constData());
sPainter.setVertexPointer(3, GL_DOUBLE, vertexArrayL.constDa
ta());
sPainter.drawFromArray(StelPainter::TriangleStrip, vertexArr
ayL.size(), 0, true);
sPainter.setVertexPointer(3, GL_DOUBLE, vertexArrayR.constDa
ta());
sPainter.drawFromArray(StelPainter::TriangleStrip, vertexArr
ayR.size(), 0, true);
sPainter.setVertexPointer(3, GL_DOUBLE, vertexArrayTop.const
Data());
sPainter.drawFromArray(StelPainter::TriangleStrip, vertexArr
ayTop.size(), 0, true);
}
sPainter.setColorPointer(4, GL_FLOAT, lineColorArray.toVector().cons
tData());
sPainter.setVertexPointer(3, GL_DOUBLE, vertexArrayLine.constData())
;
sPainter.drawFromArray(StelPainter::LineStrip, vertexArrayLine.size(
), 0, true);
glDisable(GL_BLEND);
sPainter.enableClientStates(false);
} }
bool Meteor::isAlive(void) // returns true if visible
// Assumes that we are in local frame
void Meteor::draw(const StelCore* core, StelPainter& sPainter)
{ {
return(alive); if (!m_alive)
{
return;
}
float thickness, bolideSize;
calculateThickness(core, thickness, bolideSize);
drawTrain(core, sPainter, meteor, m_viewMatrix, thickness,
m_segments, m_lineColorArray, m_trainColorArray);
drawBolide(core, sPainter, meteor, m_viewMatrix, bolideSize);
} }
 End of changes. 55 change blocks. 
211 lines changed or deleted 373 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/