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

/*
* 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;
}