Creating a 3D Fly-by Movie
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
double pi = 3.14158748587;
typedef struct start_t {
double RA;
double DEC;
double MAG;
}Star;
typedef struct camera_t {
double fovx;
double fovy;
double f;
int width;
int height;
double RA;
double distance;
}Camera;
typedef struct image_t {
int width;
int height;
unsigned char *data;
}Image;
typedef struct moon_t {
Image texture;
double radius;
}Moon;
// print an error message
void error(string message) {
cout << "Error: "<< message << endl;
exit(1);
}
// initialize the image structure
void setImage(int width,int height,Image & img) {
img.width=width;
img.height=height;
img.data=new unsigned char[width*height];
for(int i=0; i> img.width;
ss >> img.height;
setImage(img.width,img.height,img);
in.read((char*)img.data,img.width*img.height);
in.close();
}
// convert from HH:MM:SS.FRAC to double
double convertFormat(string str) {
double parts[3];
int part=0;
int start=0;
for (int i=0; i<=str.length(); ++i) {
if(str[i]==':' || str[i]=='\0') {
str[i]='\0';
parts[part++]=atof(str.substr(start,i-start).c_str());
start=i+1;
}
}
if(parts[0]<0)
return -(-parts[0]+parts[1]/60.+parts[2]/3600.);
else
return parts[0]+parts[1]/60.+parts[2]/3600.;
}
// load the bright star catalog
vector readCatalog() {
vector stars;
ifstream catalog;
string buffer;
catalog.open("kpno_brightstar_catalog");
if(!catalog.is_open())
error("Unable to open catalog file!");
getline(catalog,buffer); // get rid of header
getline(catalog,buffer);
while(getline(catalog,buffer)) {
if(buffer.find("????")!=std::string::npos) // invalid line
continue;
Star star;
stringstream ss(buffer);
int line;
string ra,dec,temp;
ss >> line;
ss >> ra;
ss >> dec;
ss >> temp;
ss >> temp;
ss >> temp;
ss >> star.MAG;
star.RA=(convertFormat(ra)*360.0/24.0)*pi/180.0; // convert to rads
star.DEC=convertFormat(dec)*pi/180.0; // convert to rads
stars.push_back(star);
}
return stars;
}
void setCamera(double fovx,double fovy,int width,Camera & camera) {
camera.width=width;
camera.fovx=fovx*pi/180.0;
camera.f = 0.5*camera.width/tan(0.5*camera.fovx); // same focus for both directions
camera.fovy=fovy*pi/180.0;
camera.height=ceil(2*tan(0.5*camera.fovy)*camera.f); // round up
camera.RA = 0.0;
}
// position camera at the right ascencion RA in degrees
void positionCamera(double RA,double position,Camera & camera) {
camera.RA = RA*pi/180.0;
camera.distance=position;
}
// determine if the star is in the field of view of the camera
int in_FOV(Star star,Camera camera) {
double startAngle,endAngle;
if(star.DEC < 0.5*camera.fovy && star.DEC>-0.5*camera.fovy) { // vertical visibility
startAngle=camera.RA-0.5*camera.fovx;
endAngle=camera.RA+0.5*camera.fovx;
if(startAngle<0) {
startAngle=2*pi+startAngle;
if(star.RA > startAngle && star.RA < 2*pi)
return 1;
else if(star.RA >= 0 && star.RA < endAngle)
return 1;
else
return 0;
}
else if(endAngle>2*pi) {
endAngle=endAngle-2*pi;
if(star.RA > startAngle && star.RA < 2*pi)
return 1;
else if(star.RA >= 0 && star.RA < endAngle)
return 1;
else
return 0;
}
else if(star.RA > startAngle && star.RA < endAngle)
return 1;
}
return 0;
}
// put a pixel in the image, trim if necessary
void putPixel(int x,int y,unsigned char color,Image img) {
if(x<0 || x>=img.width)
return;
if(y<0 || y>=img.height)
return;
int pos=(img.height-1-y)*img.width+img.width-1-x;
unsigned newColor=color+img.data[pos];
if(newColor>255)
newColor=255;
img.data[pos]=newColor;
}
// put a pixel in the image, discard previous content
void setPixel(int x,int y,unsigned char color,Image img) {
if(x<0 || x>=img.width)
return;
if(y<0 || y>=img.height)
return;
int pos=(img.height-1-y)*img.width+img.width-1-x;
img.data[pos]=color;
}
// get a pixel from the image
unsigned char getPixel(int x,int y,Image img) {
if(x<0 || x>=img.width)
return 0;
if(y<0 || y>=img.height)
return 0;
int pos=y*img.width+img.width-1-x;
return img.data[pos];
}
// draw a star as a small blob
void drawStar(Star star,Camera camera,Image img) {
double k = 2.0*star.MAG; // luminosity decay
double startAngle=camera.RA-0.5*camera.fovx;
double endAngle=camera.RA+0.5*camera.fovx;
double y0 = (0.5*camera.fovy+star.DEC);
double x0 = (star.RA - startAngle );
if(startAngle<0) {
startAngle=2*pi+startAngle;
if(star.RA > startAngle && star.RA < 2*pi)
x0= (star.RA - startAngle );
}
else if(endAngle>2*pi) {
endAngle=endAngle-2*pi;
if(star.RA > startAngle && star.RA < 2*pi)
x0= (star.RA - startAngle );
else if(star.RA >= 0 && star.RA < endAngle)
x0= (star.RA - startAngle + 2*pi );
}
x0=x0*img.width/camera.fovx;
y0=y0*img.height/camera.fovy;
for(int x=-14; x<=14; ++x) {
for(int y=-14; y<=14; ++y) {
double r=x*x+y*y;
int color=255*exp(-r/k);
putPixel(x0+x,y0+y,color,img);
}
}
}
// draw the moon using the texture
void drawMoon(Camera camera,Image img,Moon moon) {
double theta = 2.0*atan(moon.radius/camera.distance);
int d=theta*img.width/camera.fovx;
for(double x=-d/2; x<=d/2; ++x)
for(double y=-d/2; y<=d/2; ++y) {
if(x*x+y*y<=d*d/4.0) {
double yp=y*camera.distance/camera.f;
double xp=x*camera.distance/camera.f;
double rho=sqrt(xp*xp+yp*yp);
double c = asin(rho/moon.radius);
double lat = asin(yp/moon.radius);
double lon = camera.RA + pi + atan2((xp/moon.radius),cos(c));
double u = 0.5 + atan2(sin(lon),cos(lon))/(2.*pi);
double v = 0.5 - lat/pi;
int tx=(moon.texture.width-1)*u;
int ty=(moon.texture.height-1)*v;
double px,py;
px=x+img.width/2;
py=y+img.height/2;
setPixel(px,py,0.8*getPixel(tx,ty,moon.texture),img);
}
}
}
// render the picture
void render(vector stars,Camera camera,Image img,Moon moon) {
for(int i=0; i stars=readCatalog();
setCamera(24.0,16.0,1200,camera);
Image img,texture;
setImage(camera.width,camera.height,img);
loadPGM("moon_shaded_1k.pgm",texture);
Moon moon;
moon.radius=1737;
moon.texture=texture;
int frame=0;
int nframes = 500;
for(double RA=0.0; RA<=360; RA+=360./nframes) {
clearImage(img);
positionCamera(RA,80000,camera);
render(stars,camera,img,moon);
stringstream ss;
ss << "img_";
ss << ceil(camera.fovx*180/pi) << "x";
ss << ceil(camera.fovy*180/pi) << "_";
ss << setw(4) << setfill('0') << right << frame << ".pgm";
savePGM(ss.str().c_str(),img);
frame++;
}
return (0);
}