#include #include #include #include #include "dumbLog.h" #include "input.h" #include "mesh.h" #include "renderer.h" #include "util.h" #include "gooey.h" #include "orbits.h" const double SCALING = 0.001; static orbital_elements g_orbit = {}; static ellipse_3d g_ellipse3d; static uint g_time_step = 100; simple_mesh* constructEllipseMesh(ellipse_3d& e3d, glm::vec3 vertex_color) { simple_mesh* sm = meInitMesh(e3d.vert_count); for (uint i = 0; i < e3d.vert_count; i++) { sm->vertices[i] = e3d.vertices[i]; sm->vert_colors[i] = vertex_color; } return sm; } simple_mesh* constructCircleMesh(float r, uint vert_count, glm::vec3 vertex_color) { simple_mesh* sm = meInitMesh(vert_count); float angle = (float) M_PI * 2 / vert_count; r = r / SCALING; for (uint i = 0; i < vert_count; i++) { sm->vertices[i] = glm::vec3(cos(i * angle) * r, sin(i * angle) * r, 0); sm->vert_colors[i] = vertex_color; } return sm; } simple_mesh* createSatelliteMesh() { simple_mesh* sm = meInitMesh(3); glm::vec3 s_color(255, 0, 0); sm->vertices[0] = glm::vec3(0, 1 / SCALING, 0); sm->vert_colors[0] = s_color; sm->vertices[1] = glm::vec3(-1 / SCALING, -1 / SCALING, 0); sm->vert_colors[1] = s_color; sm->vertices[2] = glm::vec3(1 / SCALING, -1 / SCALING, 0); sm->vert_colors[2] = s_color; return sm; } void updateSatellitePosition(entity& satellite) { const static glm::mat4 xform = glm::rotate(glm::mat4(1.0), (float) M_PI_2, glm::vec3(1, 0, 0)); // TODO: decouple framerate from time_step g_orbit.nu = getPropagatedTrueAnomaly(g_orbit, g_orbit.nu, g_time_step); g_orbit.gamma = orbitGetFlightPathAngle(g_orbit.ep.e, g_orbit.nu); g_orbit.r = getRadialDistance(g_orbit.ep.e, g_orbit.ep.p, g_orbit.nu); g_orbit.v = orbitGetVelocity(g_orbit.epsilon, g_orbit.mu, g_orbit.r); glm::vec2 coords = polarToRect(g_orbit.nu, g_orbit.r); glm::vec3 v = g_orbit.pos = glm::vec3(coords, 0); entSetWorldPosition(satellite, xform * glm::vec4(v.x, v.y, v.z, 1)); } // NOTE: use ellipseValidate(orbit.ep) before calling to avoid failing // assertions void updateOrbit(orbital_elements& orbit, ellipse_3d& e3d, entity& ellipse_entity) { orbit.ep = ellipseInitAE(orbit.ep.a, orbit.ep.e); ellipse3DUpdate(orbit.ep, e3d); for (uint i = 0; i < e3d.vert_count; i++) ellipse_entity.mesh->vertices[i] = e3d.vertices[i]; entUpdateSimpleMesh(ellipse_entity, ellipse_entity.mesh, GL_LINE_LOOP); } void preFrameCallback(render_state* rs) { // handle input static input_state is = {}; SDL_Event e; bool gooey_wants = false; while (SDL_PollEvent(&e)) { gooey_wants = gooProcessEvent(e); if (!gooey_wants) inputProcessEvent(&is, e); } if (is.window_closed || is.escape) rs->running = false; if (g_time_step > 0) updateSatellitePosition(rs->render_groups[0].entities[2]); } void postFrameCallback(render_state* rs) { static orbital_elements c_orb = {}; c_orb = g_orbit; gooDraw(rs->handles->window, c_orb, g_time_step); bool update_if_paused = false; if (c_orb.nu != g_orbit.nu && ((c_orb.nu >= 0 && c_orb.nu <= M_PI) || (c_orb.nu < 0 && c_orb.nu >= -1 * M_PI))) { g_orbit.nu = c_orb.nu; update_if_paused = true; } // TODO: will need more validation from GUI as added if (!ellipsesEqual(c_orb.ep, g_orbit.ep)) { // NOTE: handle the case where semimajor axis lowered with low // eccentricity would fail validation if (c_orb.ep.a < g_orbit.ep.a && c_orb.ep.a > 0 && c_orb.ep.e == g_orbit.ep.e) { c_orb.ep.b = c_orb.ep.a * sqrt(1 - pow(c_orb.ep.e, 2.0)); } if (ellipseValidate(c_orb.ep)) { c_orb.ep = ellipseInitAE(c_orb.ep.a, c_orb.ep.e); g_orbit.ep = c_orb.ep; entity& ellipse_entity = rs->render_groups[0].entities[0]; updateOrbit(g_orbit, g_ellipse3d, ellipse_entity); // TODO: would be nice to have an interface for // updating/constructing an orbit g_orbit.epsilon = orbitGetSpecificEnergy(g_orbit.ep.a, g_orbit.mu); g_orbit.h = orbitGetAngularMomentum(g_orbit.ep.p, g_orbit.mu); update_if_paused = true; } } if (g_time_step == 0 && update_if_paused) updateSatellitePosition(rs->render_groups[0].entities[2]); } int main() { render_state* rs = renInit("orbital shipping", glm::vec2(1280, 800), SDL_INIT_TIMER); if (rs == nullptr) { LOG(Error) << "Error Initialzing renderer\n"; return 1; } if (!gooInit(rs->handles->window, rs->handles->glContext)) { LOG(Error) << "Error Initialzing gooey\n"; return 1; } glm::vec2 dims = renGetWindowDims(rs); cameraInitPerspective( rs->cam, glm::vec3(0, -75 / SCALING, 0), glm::vec3(0, 0, 0), glm::vec3(0,0,1), dims.x / dims.y ); // NOTE: semi-major axis in km, eccentricity, gravitational parameter g_orbit = orbitInit(26564.5, 0.7411, 398601.68); ellipse_parameters ep = ellipseInitAB(g_orbit.ep.a, g_orbit.ep.b); // TODO: (renderer) this needs to be more convenient shader_wrapper sw = { SIMPLE_SHADER, nullptr, rs->simple_shader }; rs->render_groups = renAllocateGroup(3, sw); rs->render_group_count = 1; entity& ellipse_entity = rs->render_groups[0].entities[0]; g_ellipse3d = ellipseInit3D(ep, 256); simple_mesh* sm = constructEllipseMesh(g_ellipse3d, glm::vec3(255, 0, 255)); entInitMesh(ellipse_entity, sm, GL_LINE_LOOP); entRotate(ellipse_entity, (float) M_PI_2, glm::vec3(1, 0, 0)); entity& planet_entity = rs->render_groups[0].entities[1]; sm = constructCircleMesh(3, 24, glm::vec3(255, 255, 0)); entInitMesh(planet_entity, sm, GL_LINE_LOOP); entRotate(planet_entity, (float) M_PI_2, glm::vec3(1, 0, 0)); entity& satellite_entity = rs->render_groups[0].entities[2]; sm = createSatelliteMesh(); entInitMesh(satellite_entity, sm, GL_TRIANGLES); entRotate(satellite_entity, (float) M_PI_2, glm::vec3(1, 0, 0)); entSetWorldPosition(satellite_entity, g_ellipse3d.vertices[0]); renDoRenderLoop(rs, 60 , preFrameCallback, postFrameCallback); gooFree(); renShutdown(rs); return 0; }