Lephisto/src/planet.cpp
2017-11-22 23:25:26 +00:00

465 lines
11 KiB
C++

#include "libs.h"
#include "planet.h"
#include "frame.h"
#include "l3d.h"
#include "world_view.h"
Planet::Planet(StarSystem::SBody::SubType subtype) : Body() {
m_radius = 6378135.0;
m_pos = vector3d(0, 0, 0);
m_geom = dCreateSphere(0, m_radius);
dGeomSetData(m_geom, static_cast<Body*>(this));
m_subtype = subtype;
}
Planet::~Planet(void) {
dGeomDestroy(m_geom);
}
vector3d Planet::GetPosition(void) {
return m_pos;
}
void Planet::SetPosition(vector3d p) {
m_pos = p;
dGeomSetPosition(m_geom, p.x, p.y, p.z);
}
void Planet::SetRadius(double radius) {
m_radius = radius;
dGeomSphereSetRadius(m_geom, radius);
}
void subdivide(vector3d& v1, vector3d& v2, vector3d& v3, vector3d& v4, int depth) {
if(depth) {
depth--;
vector3d v5 = v1+v2;
vector3d v6 = v2+v3;
vector3d v7 = v3+v4;
vector3d v8 = v4+v1;
vector3d v9 = v1+v2+v3+v4;
v5.Normalize();
v6.Normalize();
v7.Normalize();
v8.Normalize();
v9.Normalize();
/* XXX WRONG, need to do projection stuff! */
#if 0
/* Front-facing. */
bool ff1, ff2, ff3, ff4, ff5, ff6, ff7, ff8, ff9;
const matrix4x4d& r = L3D::world_view->viewingRotation;
ff1 = (r*v1).z > 0;
ff2 = (r*v2).z > 0;
ff3 = (r*v3).z > 0;
ff4 = (r*v4).z > 0;
ff5 = (r*v5).z > 0;
ff6 = (r*v6).z > 0;
ff7 = (r*v7).z > 0;
ff8 = (r*v8).z > 0;
ff9 = (r*v9).z > 0;
#endif
subdivide(v1, v5, v8, v8, depth);
subdivide(v5, v2, v6, v9, depth);
subdivide(v9, v6, v3, v7, depth);
subdivide(v8, v9, v7, v4, depth);
} else {
glBegin(GL_TRIANGLE_STRIP);
glNormal3dv(&v1.x);
glVertex3dv(&v1.x);
glNormal3dv(&v2.x);
glVertex3dv(&v2.x);
glNormal3dv(&v4.x);
glVertex3dv(&v4.x);
glNormal3dv(&v3.x);
glVertex3dv(&v3.x);
glEnd();
}
}
void DrawLovelyRoundCube(double radius) {
vector3d p1(1, 1, 1);
vector3d p2(-1, 1, 1);
vector3d p3(-1, -1, 1);
vector3d p4(1, -1, 1);
vector3d p5(1, 1, -1);
vector3d p6(-1, 1, -1);
vector3d p7(-1, -1, -1);
vector3d p8(1, -1, -1);
p1.Normalize();
p2.Normalize();
p3.Normalize();
p4.Normalize();
p5.Normalize();
p6.Normalize();
p7.Normalize();
p8.Normalize();
//p1 *= radius;
//p2 *= radius;
//p3 *= radius;
//p4 *= radius;
//p5 *= radius;
//p6 *= radius;
//p7 *= radius;
//p8 *= radius;
//glDisable(GL_CULL_FACE);
glEnable(GL_NORMALIZE);
subdivide(p1, p2, p3, p3, 4);
subdivide(p4, p3, p7, p8, 4);
subdivide(p1, p4, p8, p5, 4);
subdivide(p2, p1, p5, p6, 4);
subdivide(p3, p2, p6, p7, 4);
subdivide(p8, p7, p6, p5, 4);
glDisable(GL_NORMALIZE);
}
/* Both arguments in radians. */
void DrawHoop(float latitude, float width, float wobble, MTRand& rng) {
glPushAttrib(GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glEnable(GL_NORMALIZE);
glEnable(GL_BLEND);
glBegin(GL_TRIANGLE_STRIP);
for(double longitude=0.0f; longitude < 2*M_PI; longitude += 0.02) {
vector3d v;
double l;
l = latitude+0.5*width+rng(wobble*width);
v.x = sin(longitude)*cos(l);
v.y = sin(l);
v.z = cos(longitude)*cos(l);
v.Normalize();
glNormal3dv(&v.x);
glVertex3dv(&v.x);
l = latitude-0.5*width-rng(wobble*width);
v.x = sin(longitude)*cos(l);
v.y = sin(l);
v.z = cos(longitude)*cos(l);
glNormal3dv(&v.x);
glVertex3dv(&v.x);
}
double l = latitude+0.5*width;
vector3d v;
v.x = 0; v.y = sin(l); v.z = cos(l);
v.Normalize();
glNormal3dv(&v.x);
glVertex3dv(&v.x);
l = latitude-0.5*width;
v.x = 0; v.y = sin(l); v.z = cos(l);
glNormal3dv(&v.x);
glVertex3dv(&v.x);
glEnd();
glDisable(GL_BLEND);
glDisable(GL_NORMALIZE);
glPopAttrib();
}
static void PutPolarPoint(float latitude, float longitude) {
vector3d v;
v.x = sin(longitude)*cos(latitude);
v.y = sin(latitude);
v.z = cos(longitude)*cos(latitude);
v.Normalize();
glNormal3dv(&v.x);
glVertex3dv(&v.x);
}
void DrawBlob(float latitude, float longitude, float a, float b) {
glPushAttrib(GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glEnable(GL_NORMALIZE);
glEnable(GL_BLEND);
glBegin(GL_TRIANGLE_FAN);
PutPolarPoint(latitude, longitude);
for(double theta = 2*M_PI; theta > 2; theta -= 0.1) {
double _lat = latitude + a * cos(theta);
double _long = longitude + b * sin(theta);
PutPolarPoint(_lat, _long);
}
{
double _lat = latitude + a;
double _long = longitude;
PutPolarPoint(_lat, _long);
}
glEnd();
glDisable(GL_BLEND);
glDisable(GL_NORMALIZE);
glPopAttrib();
}
static void DrawRing(double inner, double outer, const float color[4]) {
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_ENABLE_BIT | GL_LIGHTING_BIT | GL_POLYGON_BIT);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glEnable(GL_NORMALIZE);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
glDisable(GL_CULL_FACE);
glColor4fv(color);
glBegin(GL_TRIANGLE_STRIP);
glNormal3f(0, 1, 0);
for(float ang = 0; ang < 2*M_PI; ang+=0.1) {
glVertex3f(inner*sin(ang), 0, inner*cos(ang));
glVertex3f(outer*sin(ang), 0, outer*cos(ang));
}
glVertex3f(0, 0, inner);
glVertex3f(0, 0, outer);
glEnd();
//gluDisk(L3D::gluQuadric, inner, outer, 40, 20);
glEnable(GL_CULL_FACE);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glDisable(GL_BLEND);
glDisable(GL_NORMALIZE);
glPopAttrib();
}
static void SphereTriSubdivide(vector3d& v1, vector3d &v2, vector3d& v3, int depth) {
if(--depth > 0) {
vector3d v4 = vector3d::Normalize(v1+v2);
vector3d v5 = vector3d::Normalize(v2+v3);
vector3d v6 = vector3d::Normalize(v1+v3);
SphereTriSubdivide(v1, v4, v6, depth);
SphereTriSubdivide(v4, v2, v5, depth);
SphereTriSubdivide(v6, v4, v5, depth);
SphereTriSubdivide(v6, v5, v3, depth);
} else {
glNormal3dv(&v1.x);
glVertex3dv(&v1.x);
glNormal3dv(&v2.x);
glVertex3dv(&v2.x);
glNormal3dv(&v3.x);
glVertex3dv(&v3.x);
}
}
/*
* yPos should be 1.0 for north pole, -1.0 for south pole.
* size in radians.
*/
static void DrawPole(double yPos, double size) {
glPushAttrib(GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glEnable(GL_NORMALIZE);
glEnable(GL_BLEND);
const bool southPole = yPos < 0;
size = size*4/M_PI;
vector3d center(0, yPos, 0);
glBegin(GL_TRIANGLES);
for(float ang = 2*M_PI; ang > 0; ang -= 0.1) {
vector3d v1(size*sin(ang), yPos, size*cos(ang));
vector3d v2(size*sin(ang+0.1), yPos, size*cos(ang+0.1));
v1.Normalize();
v2.Normalize();
if(southPole)
SphereTriSubdivide(center, v2, v1, 4);
else
SphereTriSubdivide(center, v1, v2, 4);
}
glEnd();
glDisable(GL_BLEND);
glDisable(GL_NORMALIZE);
glPopAttrib();
}
struct ColRangeObj_t {
float baseCol[4]; float modCol[4]; float modAll;
void GenCol(float col[4], MTRand& rng) const {
float ma = 1 + (rng(modAll*2)-modAll);
for(int i = 0; i < 4; i++) col[i] = baseCol[i] + rng.drange(-modCol[i], modCol[i]);
for(int i = 0; i < 3; i++) col[i] = CLAMP(ma*col[i], 0, 1);
}
};
struct GasGiantDef_t {
int hoopMin, hoopMax; float hoopWobble;
int blobMin, blobMax;
float poleMin, poleMax; /* Size range in radians. Zero for no poles. */
float ringProbability;
ColRangeObj_t ringCol;
ColRangeObj_t bodyCol;
ColRangeObj_t hoopCol;
ColRangeObj_t blobCol;
ColRangeObj_t poleCol;
};
static GasGiantDef_t ggdefs[] = {
{
/* Jupiter */
30, 40, 0.05,
20, 30,
0, 0,
0.5,
{ { .61,.48,.384,.1 }, { 0, 0, 0, .9 }, 0.3 },
{ { .99,.76,.62,1 }, { 0, .1, .1, 0 }, 0.3 },
{ { .99,.76,.62,.5 }, { 0, .1, .1, 0 }, 0.3 },
{ { .99,.76,.62,1 }, { 0, .1, .1, 0 }, 0.7 },
},
{
/* Saturnish */
10, 15, 0.0,
8, 20, /* Blob range */
0.2, 0.2, /* Pole size */
0.5,
{ { .61,.48,.384,.1 }, { 0, 0, 0, .9 }, 0.3 },
{ { .87, .68, .39, 1 }, { 0, 0, 0, 0 }, 0.1 },
{ { .87, .68, .39, 1 }, { 0, 0, 0, 0 }, 0.1 },
{ { .87, .68, .39, 1 }, { 0, 0, 0, 0 }, 0.1 },
{ { .77, .58, .29, 1 }, { 0, 0, 0, 0 }, 0.1 },
},
{
/* Neptunish */
3, 6, 0.0,
2, 6,
0, 0,
0.5,
{ { .61,.48,.384,.1 }, { 0, 0, 0, .9 }, 0.3 },
{ { .31,.44,.73,1 }, { 0, 0, 0, 0 }, .05 }, /* Body col */
{ { .31,.44,.73,0.5 }, { 0, 0, 0, 0 }, .1 }, /* Hoop col */
{ { .21,.34,.54,1 }, { 0, 0, 0, 0 }, .05 }, /* Blob col */
},
{
/* Uranus-like *wink* */
0, 0, 0.0,
0, 0,
0, 0,
0.5,
{ { .61,.48,.384,.1 }, { 0, 0, 0, .9 }, 0.3 },
{ { .70,.85,.86,1 }, { .1, .1, .1, 0 }, 0 },
{ { .70,.85,.86,1 }, { .1, .1, .1, 0 }, 0 },
{ { .70,.85,.86,1 }, { .1, .1, .1, 0 }, 0 },
{ { .70,.85,.86,1 }, { .1, .1, .1, 0 }, 0 }
},
{
/* Brown dwarf-like */
0, 0, 0.05,
10, 20,
0, 0,
0.5,
{ { .81,.48,.384,.1 }, { 0, 0, 0, .9 }, 0.3 },
{ { .4,.1,0,1 }, { 0, 0, 0, 0 }, 0.1 },
{ { .4,.1,0,1 }, { 0, 0, 0, 0 }, 0.1 },
{ { .4,.1,0,1 }, { 0, 0, 0, 0 }, 0.1 },
},
};
static GasGiantDef_t &ggdef = ggdefs[0];
#define PLANET_AMBIENT 0.1
static void SetMaterialColor(const float col[4]) {
float mambient[4];
mambient[0] = col[0]*PLANET_AMBIENT;
mambient[1] = col[1]*PLANET_AMBIENT;
mambient[2] = col[2]*PLANET_AMBIENT;
mambient[3] = col[3];
glMaterialfv(GL_FRONT, GL_AMBIENT, mambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, col);
}
static void DrawGasGiant(void) {
MTRand rng((int)L3D::GetGameTime());
float col[4];
ggdef.bodyCol.GenCol(col, rng);
SetMaterialColor(col);
DrawLovelyRoundCube(1.0f);
int n = rng(ggdef.hoopMin, ggdef.hoopMax);
while(n-- > 0) {
ggdef.hoopCol.GenCol(col, rng);
SetMaterialColor(col);
DrawHoop(rng(0,9*M_PI)-0.45*M_PI, rng(0.25), ggdef.hoopWobble, rng);
}
n = rng(ggdef.blobMin, ggdef.blobMax);
while(n-- > 0) {
float a = rng.drange(0.01, 0.03);
float b = a+rng(0.2)+0.1;
ggdef.blobCol.GenCol(col, rng);
SetMaterialColor(col);
DrawBlob(rng.drange(-0.3*M_PI, 0.3*M_PI), rng(2*M_PI), a, b);
}
if(ggdef.poleMin != 0) {
float size = rng.drange(ggdef.poleMin, ggdef.poleMax);
ggdef.poleCol.GenCol(col, rng);
SetMaterialColor(col);
DrawPole(1.0, size);
DrawPole(-1.0, size);
}
if(rng(1.0) < ggdef.ringProbability) {
float pos = rng.drange(1.2, 1.7);
float end = pos + rng.drange(0.1, 1.0);
end = MIN(end, 2.5);
while(pos < end) {
float size = rng(0.1);
ggdef.ringCol.GenCol(col, rng);
DrawRing(pos, pos+size, col);
pos += size;
}
}
}
void Planet::Render(const Frame* a_camFrame) {
glPushMatrix();
double rad = m_radius;
vector3d fpos = GetPositionRelTo(a_camFrame);
double apparent_size = rad / fpos.Length();
double len = fpos.Length();
while(len > 5000.0f) {
rad *= 0.25;
fpos = 0.25*fpos;
len *= 0.25;
}
glTranslatef(fpos.x, fpos.y, fpos.z);
glColor3f(1, 1, 1);
if(apparent_size < 0.001) {
glDisable(GL_LIGHTING);
glPointSize(1.0);
glBegin(GL_POINTS);
glVertex3f(0, 0, 0);
glEnd();
glEnable(GL_LIGHTING);
} else {
glScalef(rad, rad, rad);
DrawGasGiant();
glClear(GL_DEPTH_BUFFER_BIT);
}
glPopMatrix();
}
void Planet::SetFrame(Frame* f) {
if(GetFrame()) GetFrame()->RemoveGeom(m_geom);
Body::SetFrame(f);
if(f) f->AddGeom(m_geom);
}