You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
8.0 KiB
297 lines
8.0 KiB
|
|
/* |
|
* TODO: |
|
* - add time to apoapsis/periapsis to satellite properties |
|
* - use consistant name style for structs) 'UpperCase' |
|
* - make an 'overlay' graphic for things like apoapsis, perisapsis, f1, f2, |
|
* flight path |
|
* - impulsive orbital maneuvers |
|
* - Hohmman transfer orbits |
|
* - patched conic method for transferring between 2 grav bodies |
|
*/ |
|
|
|
#include <cassert> |
|
#include <cmath> |
|
|
|
#include <SDL2/SDL.h> |
|
#include <glm/glm.hpp> |
|
#include <glm/gtc/matrix_transform.hpp> |
|
|
|
#include "input.h" |
|
#include "tangerine.h" |
|
|
|
#include "gooey.h" |
|
#include "orbits.h" |
|
|
|
|
|
const double SCALING = 0.001; |
|
const vec4 g_light_direction = vec4(-2, 1, 3, 1); |
|
const vec4 g_light_color = vec4(0.5, 0.5, 0.5, 1); |
|
const uvec4 g_light_intensities = uvec4(1, 0, 0, 0); |
|
|
|
struct GameState |
|
{ |
|
bool running; |
|
MemoryArena* arena; |
|
|
|
u64 game_time_ms; |
|
u64 sim_time_ms; |
|
float sim_speed; |
|
|
|
system_2body system; // FIXME: we're going to need more than 1 system... |
|
Entity* satellite_entity; |
|
}; |
|
|
|
|
|
void |
|
initCamera(RenderState* rs, vec3 cam_pos, vec3 cam_focus, vec3 cam_up) |
|
{ |
|
float aspect_ratio = (float) rs->window_dims.x / (float) rs->window_dims.y; |
|
cameraInitPerspective(rs->camera, cam_pos, cam_focus, cam_up, aspect_ratio); |
|
GLBuffer* xform_ubo = getUBOByName(rs->gl_ctx, "matrices"); |
|
assert(xform_ubo != nullptr); |
|
updateGLBuffer(xform_ubo, &rs->camera->xforms); |
|
} |
|
void |
|
initLights(RenderState* rs) |
|
{ |
|
LightsBuffer* lb = rs->lights_buf; |
|
u32 idx = 0; |
|
|
|
// NOTE: add a directional light |
|
idx = (*lb->active_d_lights)++; |
|
lb->dl_directions[idx] = g_light_direction; |
|
lb->dl_colors[idx] = g_light_color; |
|
lb->dl_intensities[idx] = g_light_intensities; |
|
|
|
GLBuffer* lights_ubo = getUBOByName(rs->gl_ctx, "lights"); |
|
assert(lights_ubo != nullptr); |
|
updateGLBuffer(lights_ubo, lb->buffer); |
|
} |
|
|
|
Entity* |
|
initEllipseEntity(RenderState* rs, |
|
system_2body* system, |
|
u32 num_vertices) |
|
{ |
|
system->e3d = ellipseInit3D(system->ep, num_vertices); |
|
Mesh* m = meshInit(rs->rg_arena, num_vertices, num_vertices, false, true); |
|
vec3 vertex_color = vec3(255, 0, 255); |
|
|
|
for (uint i = 0; i < system->e3d.vert_count; i++) { |
|
m->vertices[i] = system->e3d.vertices[i]; |
|
m->colors[i] = vertex_color; |
|
m->indices[i] = i; |
|
} |
|
|
|
Model* mdl = modelInitManual(rs->rg_arena, 1, m); |
|
RenderGroup* rg = getRenderGroupByName(rs, "manual mesh group"); |
|
Entity* e = getFreeEntity(rg); |
|
initEntity(e, rs->gl_ctx, |
|
rs->rg_arena, |
|
mdl, |
|
rg->shader->num_vertex_attribs, |
|
rg->shader->attrib_mappings, |
|
"ellipse 01", |
|
GL_LINE_LOOP); |
|
|
|
return e; |
|
} |
|
|
|
Entity* |
|
initPlanetEntity(RenderState* rs, double r) |
|
{ |
|
ShaderProgram* shader_lit = getShaderByName("full_lighting", rs->gl_ctx); |
|
RenderGroup* rg_full = getFreeRenderGroup(rs); |
|
initRenderGroup(rg_full, rs->rg_arena, shader_lit, 256, "lit group"); |
|
Model* icosphere = getModelByPath(&rs->assets, "../data/icosphere.gltf"); |
|
Entity* e = getFreeEntity(rg_full); |
|
assert(icosphere && e); |
|
initEntity(e, |
|
rs->gl_ctx, |
|
rs->rg_arena, |
|
icosphere, |
|
shader_lit->num_vertex_attribs, |
|
shader_lit->attrib_mappings, |
|
"grav body 01"); |
|
scaleEntity(e, r); |
|
|
|
return e; |
|
} |
|
|
|
Entity* |
|
initSatelliteEntity(RenderState* rs) |
|
{ |
|
u32 num_vertices = 3; |
|
vec3 vertex_color = vec3(255, 0, 0); |
|
Mesh* m = meshInit(rs->rg_arena, num_vertices, num_vertices, false, true); |
|
m->vertices[0] = vec3(0, 1, 0); |
|
m->colors[0] = vertex_color; |
|
m->indices[0] = 0; |
|
m->vertices[1] = vec3(-1, -1, 0); |
|
m->colors[1] = vertex_color; |
|
m->indices[1] = 1; |
|
m->vertices[2] = vec3(1, -1, 0); |
|
m->colors[2] = vertex_color; |
|
m->indices[2] = 2; |
|
|
|
Model* mdl = modelInitManual(rs->rg_arena, 1, m); |
|
RenderGroup* rg = getRenderGroupByName(rs, "manual mesh group"); |
|
Entity* e = getFreeEntity(rg); |
|
initEntity(e, rs->gl_ctx, |
|
rs->rg_arena, |
|
mdl, |
|
rg->shader->num_vertex_attribs, |
|
rg->shader->attrib_mappings, |
|
"ship 01", |
|
GL_TRIANGLES); |
|
|
|
return e; |
|
} |
|
|
|
void |
|
loadScene(GameState* gs, RenderState* rs) |
|
{ |
|
initCamera(rs, vec3(0, -75 / SCALING, 0), vec3(0, 0, 0), vec3(0,0,1)); |
|
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; |
|
|
|
// TODO: streamline the boilerplate in init_X_Entity functions |
|
ShaderProgram* shader = getShaderByName("colored_vertices", rs->gl_ctx); |
|
initRenderGroup( |
|
getFreeRenderGroup(rs), rs->rg_arena, shader, 256, "manual mesh group"); |
|
|
|
//Entity* ellipse_entity = initEllipseEntity(rs, g_sys.ep, 256); |
|
Entity* ellipse_entity = initEllipseEntity(rs, &gs->system, 256); |
|
setEntityPosition(ellipse_entity, vec3(0, 0, 0)); |
|
rotateEntity(ellipse_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
|
|
initPlanetEntity(rs, r); |
|
|
|
gs->satellite_entity = initSatelliteEntity(rs); |
|
scaleEntity(gs->satellite_entity, 1 / SCALING); |
|
rotateEntity(gs->satellite_entity, vec3(1, 0, 0), (float) M_PI / 2); |
|
setEntityPosition(gs->satellite_entity, gs->system.e3d.vertices[0]); |
|
} |
|
|
|
void |
|
updateSatelliteModel(const system_2body& sys, |
|
satellite& sat, |
|
double time_step) |
|
{ |
|
sat.theta = getPropagatedTrueAnomaly(sys, sat.theta, time_step); |
|
sat.gamma = orbitGetFlightPathAngle(sys.ep.e, sat.theta); |
|
sat.r = orbitGetRadialDistance(sys.ep.e, sys.ep.p, sat.theta); |
|
sat.v = orbitGetVelocity(sys.epsilon, sys.body.mu, sat.r); |
|
sat.position = glm::vec3(polarToRect(sat.theta, sat.r), 0); |
|
} |
|
|
|
void |
|
updateSatelliteEntity(Entity* e, const satellite& sat) |
|
{ |
|
const static mat4 xform = |
|
glm::rotate(mat4(1.0), (float) M_PI_2, vec3(1, 0, 0)); |
|
const vec3& v = sat.position; |
|
setEntityPosition(e, xform * vec4(v.x, v.y, v.z, 1)); |
|
} |
|
|
|
// NOTE: use ellipseValidate(ep) before calling to avoid failing assertions |
|
void |
|
updateOrbit(system_2body sys, Entity& ellipse_entity) |
|
{ |
|
#if 0 |
|
ellipse3DUpdate(sys.ep, sys.e3d); |
|
|
|
for (uint i = 0; i < sys.e3d.vert_count; i++) |
|
ellipse_entity.mesh->vertices[i] = sys.e3d.vertices[i]; |
|
|
|
entUpdateSimpleMesh(ellipse_entity, ellipse_entity.mesh, GL_LINE_LOOP); |
|
#endif |
|
} |
|
|
|
void |
|
preFrameCallback(RenderState* rs, void* user_data = nullptr) |
|
{ |
|
static InputState is = {}; |
|
SDL_Event e; |
|
bool gooey_wants = false; |
|
|
|
while (SDL_PollEvent(&e)) { |
|
// FIXME: we should check for escape key here first |
|
gooey_wants = gooProcessEvent(e); |
|
if (!gooey_wants) inputProcessEvent(&is, e); |
|
} |
|
|
|
if (is.window_closed || is.escape) |
|
rs->running = false; |
|
|
|
// TODO: sim time stuff should have dedicated functions, and maybe a test |
|
// since it's pretty important that the simulation is accurate-ish |
|
// TODO: verify time to apoapsis/periapsis are correct |
|
assert(user_data != nullptr); |
|
GameState* gs = (GameState*) user_data; |
|
u64 last_sdl_tick = gs->game_time_ms; |
|
gs->game_time_ms = SDL_GetTicks64(); |
|
u64 current_tick = gs->game_time_ms - last_sdl_tick; |
|
|
|
// NOTE: update sim time base on time since last frame * sim_speed |
|
if (gs->running) { |
|
u64 last_game_tick = gs->sim_time_ms; |
|
gs->sim_time_ms = gs->sim_time_ms + current_tick * gs->sim_speed; |
|
u32 diff_ms = gs->sim_time_ms - last_game_tick; |
|
double time_step = double(diff_ms) / 1000; |
|
|
|
updateSatelliteModel(gs->system, gs->system.sat, time_step); |
|
gs->system.ep = ellipseInitAE(gs->system.ep.a, gs->system.ep.e); |
|
updateSatelliteEntity(gs->satellite_entity, gs->system.sat); |
|
//updateOrbit(gs->system, ellipse_entity); |
|
} |
|
} |
|
|
|
void |
|
postFrameCallback(RenderState* rs, void* user_data = nullptr) |
|
{ |
|
assert(user_data != nullptr); |
|
GameState* gs = (GameState*) user_data; |
|
gooDraw(rs->handles.window, |
|
gs->system, |
|
gs->running, |
|
gs->sim_time_ms, |
|
gs->sim_speed); |
|
} |
|
|
|
#define DEFAULT_SIM_SPEED 100 |
|
int |
|
main() |
|
{ |
|
RenderState* rs = initRenderState("orbital shipping", |
|
uvec2(1600, 900), |
|
SDL_INIT_VIDEO | SDL_INIT_TIMER); |
|
|
|
if (rs == nullptr) { |
|
LOG(Error) << "Error Initialzing renderer\n"; |
|
return 1; |
|
} |
|
|
|
if (!gooInit(rs->handles.window, rs->handles.sdl_gl_ctx)) { |
|
LOG(Error) << "Error Initialzing gooey\n"; |
|
return 1; |
|
} |
|
|
|
GameState gs = {0}; |
|
gs.arena = arenaInit(16); |
|
gs.sim_speed = DEFAULT_SIM_SPEED; |
|
loadScene(&gs, rs); |
|
doRenderLoop(rs, 60, preFrameCallback, postFrameCallback, &gs); |
|
gooFree(); |
|
freeRenderState(rs); |
|
|
|
return 0; |
|
} |
|
|
|
|