|
|
|
@ -8,7 +8,6 @@ |
|
|
|
* - make an 'overlay' graphic for things like apoapsis, perisapsis, f1, f2, |
|
|
|
* - make an 'overlay' graphic for things like apoapsis, perisapsis, f1, f2, |
|
|
|
* flight path |
|
|
|
* flight path |
|
|
|
* - impulsive orbital maneuvers |
|
|
|
* - impulsive orbital maneuvers |
|
|
|
* - test multiple orbits |
|
|
|
|
|
|
|
* - Hohmman transfer orbits |
|
|
|
* - Hohmman transfer orbits |
|
|
|
* - patched conic method for transferring between 2 grav bodies |
|
|
|
* - patched conic method for transferring between 2 grav bodies |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -32,6 +31,17 @@ const vec4 g_light_direction = vec4(-2, 1, 3, 1); |
|
|
|
const vec4 g_light_color = vec4(0.5, 0.5, 0.5, 1); |
|
|
|
const vec4 g_light_color = vec4(0.5, 0.5, 0.5, 1); |
|
|
|
const uvec4 g_light_intensities = uvec4(1, 0, 0, 0); |
|
|
|
const uvec4 g_light_intensities = uvec4(1, 0, 0, 0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct GameOrbit |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
TwoBodySystem system; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Entity* grav_body; |
|
|
|
|
|
|
|
Entity* ellipse_entity; |
|
|
|
|
|
|
|
Entity* satellite_entity; |
|
|
|
|
|
|
|
bool in_use; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct GameState |
|
|
|
struct GameState |
|
|
|
{ |
|
|
|
{ |
|
|
|
bool running; |
|
|
|
bool running; |
|
|
|
@ -41,10 +51,41 @@ struct GameState |
|
|
|
u64 sim_time_ms; |
|
|
|
u64 sim_time_ms; |
|
|
|
float sim_speed; |
|
|
|
float sim_speed; |
|
|
|
|
|
|
|
|
|
|
|
TwoBodySystem system; // FIXME: we're going to need more than 1 system...
|
|
|
|
GameOrbit* orbits; |
|
|
|
Entity* satellite_entity; |
|
|
|
u32 num_orbits; |
|
|
|
|
|
|
|
u32 max_orbits; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GameOrbit* |
|
|
|
|
|
|
|
getFreeOrbit(GameState* gs) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
GameOrbit* orbit = nullptr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NOTE: first check if we have a freed orbit to use
|
|
|
|
|
|
|
|
for (u32 i = 0; i < gs->num_orbits; i++) { |
|
|
|
|
|
|
|
if (!gs->orbits[i].in_use) |
|
|
|
|
|
|
|
orbit = &gs->orbits[i]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NOTE: if we can't re-use a freed orbit, initialize a new one
|
|
|
|
|
|
|
|
if (!orbit) { |
|
|
|
|
|
|
|
assert(gs->num_orbits < gs->max_orbits); |
|
|
|
|
|
|
|
orbit = &gs->orbits[gs->num_orbits++]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
orbit->in_use = true; |
|
|
|
|
|
|
|
return orbit; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
|
|
|
|
disableGameOrbit(GameOrbit* orbit) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
orbit->in_use = false; |
|
|
|
|
|
|
|
orbit->system = {0}; |
|
|
|
|
|
|
|
orbit->grav_body = nullptr; |
|
|
|
|
|
|
|
orbit->ellipse_entity = nullptr; |
|
|
|
|
|
|
|
orbit->satellite_entity = nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
void |
|
|
|
initCamera(RenderState* rs, vec3 cam_pos, vec3 cam_focus, vec3 cam_up) |
|
|
|
initCamera(RenderState* rs, vec3 cam_pos, vec3 cam_focus, vec3 cam_up) |
|
|
|
@ -72,6 +113,7 @@ initLights(RenderState* rs) |
|
|
|
updateGLBuffer(lights_ubo, lb->buffer); |
|
|
|
updateGLBuffer(lights_ubo, lb->buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: could pass EllipseParameters and Ellipse3D instead of TwoBodySystem
|
|
|
|
Entity* |
|
|
|
Entity* |
|
|
|
initEllipseEntity(RenderState* rs, |
|
|
|
initEllipseEntity(RenderState* rs, |
|
|
|
TwoBodySystem* system, |
|
|
|
TwoBodySystem* system, |
|
|
|
@ -157,37 +199,47 @@ loadScene(GameState* gs, RenderState* rs) |
|
|
|
{ |
|
|
|
{ |
|
|
|
initCamera(rs, vec3(0, -75 / SCALING, 0), vec3(0, 0, 0), vec3(0,0,1)); |
|
|
|
initCamera(rs, vec3(0, -75 / SCALING, 0), vec3(0, 0, 0), vec3(0,0,1)); |
|
|
|
initLights(rs); |
|
|
|
initLights(rs); |
|
|
|
|
|
|
|
|
|
|
|
double a = 26564.5; // NOTE: semi-major axis in km
|
|
|
|
|
|
|
|
double e = 0.7411; // NOTE: eccentricity
|
|
|
|
|
|
|
|
double mu = 398601.68; // NOTE: gravitational parameter
|
|
|
|
|
|
|
|
double r = 6378; // NOTE: body radius in km
|
|
|
|
|
|
|
|
gs->system = systemInit(gravBodyInit(mu, r), orbitInit(a, e)); |
|
|
|
|
|
|
|
gs->running = true; |
|
|
|
gs->running = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double a = 26564.5; // semi-major axis in km
|
|
|
|
|
|
|
|
double e = 0.7411; // eccentricity
|
|
|
|
|
|
|
|
double mu = 398601.68; // gravitational parameter
|
|
|
|
|
|
|
|
double r = 6378; // body radius in km
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GameOrbit* orbit_1 = getFreeOrbit(gs); |
|
|
|
|
|
|
|
systemInit(orbit_1->system, gravBodyInit(mu, r), orbitInit(a, e)); |
|
|
|
|
|
|
|
|
|
|
|
// TODO: streamline the boilerplate in init_X_Entity functions
|
|
|
|
// TODO: streamline the boilerplate in init_X_Entity functions
|
|
|
|
ShaderProgram* shader = getShaderByName("colored_vertices", rs->gl_ctx); |
|
|
|
ShaderProgram* shader = getShaderByName("colored_vertices", rs->gl_ctx); |
|
|
|
initRenderGroup( |
|
|
|
initRenderGroup( |
|
|
|
getFreeRenderGroup(rs), rs->rg_arena, shader, 256, "manual mesh group"); |
|
|
|
getFreeRenderGroup(rs), rs->rg_arena, shader, 256, "manual mesh group"); |
|
|
|
|
|
|
|
|
|
|
|
//Entity* ellipse_entity = initEllipseEntity(rs, g_sys.ep, 256);
|
|
|
|
orbit_1->ellipse_entity = initEllipseEntity(rs, &orbit_1->system, 256); |
|
|
|
Entity* ellipse_entity = initEllipseEntity(rs, &gs->system, 256); |
|
|
|
setEntityPosition(orbit_1->ellipse_entity, vec3(0, 0, 0)); |
|
|
|
setEntityPosition(ellipse_entity, vec3(0, 0, 0)); |
|
|
|
rotateEntity(orbit_1->ellipse_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
|
|
rotateEntity(ellipse_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
initPlanetEntity(rs, r); |
|
|
|
initPlanetEntity(rs, r); |
|
|
|
|
|
|
|
|
|
|
|
gs->satellite_entity = initSatelliteEntity(rs); |
|
|
|
orbit_1->satellite_entity = initSatelliteEntity(rs); |
|
|
|
scaleEntity(gs->satellite_entity, 1 / SCALING); |
|
|
|
scaleEntity(orbit_1->satellite_entity, 1 / SCALING); |
|
|
|
rotateEntity(gs->satellite_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
|
|
rotateEntity(orbit_1->satellite_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
|
|
setEntityPosition(gs->satellite_entity, gs->system.e3d.vertices[0]); |
|
|
|
setEntityPosition(orbit_1->satellite_entity, |
|
|
|
|
|
|
|
orbit_1->system.e3d.vertices[0]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NOTE: testing multiple orbits
|
|
|
|
|
|
|
|
double a_2 = r + 10000; // km
|
|
|
|
|
|
|
|
double e_2 = 0; |
|
|
|
|
|
|
|
GameOrbit* orbit_2 = getFreeOrbit(gs); |
|
|
|
|
|
|
|
systemInit(orbit_2->system, gravBodyInit(mu, r), orbitInit(a_2, e_2)); |
|
|
|
|
|
|
|
orbit_2->ellipse_entity = initEllipseEntity(rs, &orbit_2->system, 256); |
|
|
|
|
|
|
|
setEntityPosition(orbit_2->ellipse_entity, vec3(0, 0, 0)); |
|
|
|
|
|
|
|
rotateEntity(orbit_2->ellipse_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
void |
|
|
|
updateSatelliteModel(const TwoBodySystem& sys, |
|
|
|
updateSatelliteModel(TwoBodySystem& sys, double time_step) |
|
|
|
Satellite& sat, |
|
|
|
|
|
|
|
double time_step) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
Satellite& sat = sys.sat; |
|
|
|
sat.theta = orbitGetPropagatedTrueAnomaly(sys, sat.theta, time_step); |
|
|
|
sat.theta = orbitGetPropagatedTrueAnomaly(sys, sat.theta, time_step); |
|
|
|
sat.gamma = orbitGetFlightPathAngle(sys.ep.e, sat.theta); |
|
|
|
sat.gamma = orbitGetFlightPathAngle(sys.ep.e, sat.theta); |
|
|
|
sat.r = orbitGetRadialDistance(sys.ep.e, sys.ep.p, sat.theta); |
|
|
|
sat.r = orbitGetRadialDistance(sys.ep.e, sys.ep.p, sat.theta); |
|
|
|
@ -250,26 +302,36 @@ preFrameCallback(RenderState* rs, void* user_data = nullptr) |
|
|
|
u32 diff_ms = gs->sim_time_ms - last_game_tick; |
|
|
|
u32 diff_ms = gs->sim_time_ms - last_game_tick; |
|
|
|
double time_step = double(diff_ms) / 1000; |
|
|
|
double time_step = double(diff_ms) / 1000; |
|
|
|
|
|
|
|
|
|
|
|
updateSatelliteModel(gs->system, gs->system.sat, time_step); |
|
|
|
for (u32 i = 0; i < gs->num_orbits; i++) { |
|
|
|
gs->system.ep = ellipseInitAE(gs->system.ep.a, gs->system.ep.e); |
|
|
|
GameOrbit& orbit = gs->orbits[i]; |
|
|
|
updateSatelliteEntity(gs->satellite_entity, gs->system.sat); |
|
|
|
|
|
|
|
|
|
|
|
if (orbit.in_use && orbit.satellite_entity) { |
|
|
|
|
|
|
|
updateSatelliteModel(orbit.system, time_step); |
|
|
|
|
|
|
|
updateSatelliteEntity(orbit.satellite_entity, orbit.system.sat); |
|
|
|
|
|
|
|
// TODO: update EllipseEntity per frame
|
|
|
|
//updateOrbit(gs->system, ellipse_entity);
|
|
|
|
//updateOrbit(gs->system, ellipse_entity);
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void |
|
|
|
void |
|
|
|
postFrameCallback(RenderState* rs, void* user_data = nullptr) |
|
|
|
postFrameCallback(RenderState* rs, void* user_data = nullptr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
assert(user_data != nullptr); |
|
|
|
assert(user_data != nullptr); |
|
|
|
GameState* gs = (GameState*) user_data; |
|
|
|
GameState* gs = (GameState*) user_data; |
|
|
|
|
|
|
|
GameOrbit& orbit = gs->orbits[0]; |
|
|
|
|
|
|
|
assert(orbit.in_use); |
|
|
|
gooDraw(rs->handles.window, |
|
|
|
gooDraw(rs->handles.window, |
|
|
|
gs->system, |
|
|
|
orbit.system, |
|
|
|
gs->running, |
|
|
|
gs->running, |
|
|
|
gs->sim_time_ms, |
|
|
|
gs->sim_time_ms, |
|
|
|
gs->sim_speed); |
|
|
|
gs->sim_speed); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_SIM_SPEED 2000 |
|
|
|
#define DEFAULT_SIM_SPEED 2000 |
|
|
|
|
|
|
|
#define GAME_ARENA_SIZE 16 * 1024 * 1024 // 16MB
|
|
|
|
|
|
|
|
#define DEFAULT_MAX_ORBITS 10000 |
|
|
|
int |
|
|
|
int |
|
|
|
main() |
|
|
|
main() |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -288,8 +350,11 @@ main() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
GameState gs = {0}; |
|
|
|
GameState gs = {0}; |
|
|
|
gs.arena = arenaInit(16); |
|
|
|
gs.arena = arenaInit(GAME_ARENA_SIZE); |
|
|
|
gs.sim_speed = DEFAULT_SIM_SPEED; |
|
|
|
gs.sim_speed = DEFAULT_SIM_SPEED; |
|
|
|
|
|
|
|
gs.max_orbits = DEFAULT_MAX_ORBITS; |
|
|
|
|
|
|
|
gs.orbits = ARENA_ALLOC(gs.arena, GameOrbit, DEFAULT_MAX_ORBITS); |
|
|
|
|
|
|
|
|
|
|
|
loadScene(&gs, rs); |
|
|
|
loadScene(&gs, rs); |
|
|
|
doRenderLoop(rs, 60, preFrameCallback, postFrameCallback, &gs); |
|
|
|
doRenderLoop(rs, 60, preFrameCallback, postFrameCallback, &gs); |
|
|
|
gooFree(); |
|
|
|
gooFree(); |
|
|
|
|