diff --git a/src/game.cpp b/src/game.cpp index 66942a8..6504f0e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -125,39 +125,59 @@ selectOrbit(GameState* gs, GameOrbit* orbit) gs->last_selected_orbit = orbit; } +ManeuverNode* +getFreeManeuver(GameState* gs) +{ + for (u32 i = 0; i < gs->max_maneuvers; i++) { + if (gs->maneuver_nodes[i].active == false) { + gs->maneuver_nodes[i].active = true; + return &gs->maneuver_nodes[i]; + } + } + + LOGF(Error, "No free maneuver nodes\n"); + return nullptr; +} + +// TODO: appendManeuver(orbit, node) would be better bool -addManeuver(GameOrbit* orbit, +addManeuver(GameState* gs, + GameOrbit* orbit, ImpulseType impulse_type, double true_anomaly, double impulse_delta_v) { - ManeuverNode& node = orbit->maneuver; - if (true_anomaly < -M_PI || true_anomaly > M_PI) { LOGF(Error, "invalid true true_anomaly: %f\n", true_anomaly); return false; } - if (node.active) { - LOGF(Warning, "cannot add multiple maneuvers (yet)\n"); - return false; - } - if (impulse_delta_v == 0.f) { LOGF(Warning, "refusing to add maneuver with 0 dv\n"); return false; } - node.impulse_type = impulse_type; + ManeuverNode* node = getFreeManeuver(gs); + assert(node); + + if (orbit->last_maneuver) { + node->previous_maneuver = orbit->last_maneuver; + orbit->last_maneuver->next_maneuver = node; + orbit->last_maneuver = node; + } else { + orbit->first_maneuver = node; + orbit->last_maneuver = node; + } + + node->impulse_type = impulse_type; // TODO: thrust calculations //double flight_path_angle = orbit->system.sat.gamma; //node.impulse_vector.x = ... - - if (node.impulse_type == ImpulseType::PROGRADE) { - node.true_anomaly = true_anomaly; - node.impulse_delta_v = impulse_delta_v; - node.active = true; + if (node->impulse_type == ImpulseType::PROGRADE) { + node->true_anomaly = true_anomaly; + node->impulse_delta_v = impulse_delta_v; + node->active = true; return true; } @@ -167,29 +187,17 @@ addManeuver(GameOrbit* orbit, return false; } -void -removeManeuver(GameOrbit* orbit) -{ - // TODO: multiple maneuver nodes - ManeuverNode& node = orbit->maneuver; - node.impulse_type = ImpulseType::NONE; - node.true_anomaly = 0.f; - node.impulse_delta_v = 0.f; - node.active = false; -} - // NOTE: test if the maneuver node would have occured between 2 orbit positions // eg) between 2 time steps bool -testManeuverStep(const ManeuverNode& maneuver, +testManeuverStep(ManeuverNode* maneuver, double previous_theta, double next_theta) { - if (!maneuver.active) - return false; + assert(maneuver); // NOTE: clamp angles between 0 and 2 pi to simplify inequalities - double r = orbitClampAngle(maneuver.true_anomaly); + double r = orbitClampAngle(maneuver->true_anomaly); double clamped_previous_theta = orbitClampAngle(previous_theta); double clamped_next_theta = orbitClampAngle(next_theta); @@ -209,16 +217,16 @@ testManeuverStep(const ManeuverNode& maneuver, } void -applyManeuver(GameOrbit* orbit, double previous_true_anomaly) +applyManeuver(GameOrbit* orbit, ManeuverNode* maneuver) { - const ManeuverNode& node = orbit->maneuver; TwoBodySystem& sys = orbit->system; + assert(maneuver); // FIXME: assuming prograde impulse vector - assert(node.impulse_type == ImpulseType::PROGRADE); + assert(maneuver->impulse_type == ImpulseType::PROGRADE); // re-calculate state vectors at the maneuver node - double theta = node.true_anomaly; + double theta = maneuver->true_anomaly; double mu = sys.body.mu; double r = orbitGetRadialDistance(sys.ep.e, sys.ep.p, theta); @@ -229,7 +237,7 @@ applyManeuver(GameOrbit* orbit, double previous_true_anomaly) vel = sys.rotation * vel; // apply impulse along velocity vector - dvec3 impulse = glm::normalize(vel) * node.impulse_delta_v; + dvec3 impulse = glm::normalize(vel) * maneuver->impulse_delta_v; vel = vel + impulse; sys.elements = orbitGetElementsFromStateVectors(pos, vel, mu); systemInit(orbit->system, sys.body, sys.elements); @@ -245,6 +253,29 @@ applyManeuver(GameOrbit* orbit, double previous_true_anomaly) updateGLBuffer(buf, orbit->e3d.vertices); } +void +removeManeuver(GameOrbit* orbit, ManeuverNode* maneuver) +{ + assert(maneuver); + // FIXME: we're only removing the first maneuver for now + assert(maneuver == orbit->first_maneuver); + + if (orbit->last_maneuver == maneuver) { + orbit->first_maneuver = nullptr; + orbit->last_maneuver = nullptr; + } else { + orbit->first_maneuver = maneuver->next_maneuver; + maneuver->previous_maneuver = nullptr; + } + + maneuver->impulse_type = ImpulseType::NONE; + maneuver->true_anomaly = 0.f; + maneuver->impulse_delta_v = 0.f; + maneuver->active = false; + maneuver->previous_maneuver = nullptr; + maneuver->next_maneuver = nullptr; +} + // internal diff --git a/src/game.h b/src/game.h index 6f58525..f4d80af 100644 --- a/src/game.h +++ b/src/game.h @@ -40,16 +40,16 @@ struct ManeuverNode double impulse_delta_v; bool active; - // TODO: thrust calculations - //vec3 impulse_vector; + ManeuverNode* previous_maneuver; + ManeuverNode* next_maneuver; }; struct GameOrbit { TwoBodySystem system; Ellipse3D e3d; - // TODO: multiple maneuvers - ManeuverNode maneuver; + ManeuverNode* first_maneuver; + ManeuverNode* last_maneuver; Entity* grav_body; Entity* ellipse_entity; @@ -71,6 +71,9 @@ struct GameState u32 num_orbits; u32 max_orbits; + ManeuverNode* maneuver_nodes; + u32 max_maneuvers; + GameOrbit* last_selected_orbit; Entity* coord_overlay; }; @@ -89,15 +92,16 @@ void ellipse3DUpdate(dmat3 rotation, EllipseParameters ep, Ellipse3D& e3d); void selectOrbit(GameState* gs, GameOrbit* orbit); -bool addManeuver(GameOrbit* orbit, +bool addManeuver(GameState* gs, + GameOrbit* orbit, ImpulseType impulse_type, double true_anomaly, double impulse_delta_v); -void removeManeuver(GameOrbit* orbit); - -bool testManeuverStep(const ManeuverNode& maneuver, +bool testManeuverStep(ManeuverNode* maneuver, double previous_theta, double next_theta); -void applyManeuver(GameOrbit* orbit, double previous_true_anomaly); +void applyManeuver(GameOrbit* orbit, ManeuverNode* node); + +void removeManeuver(GameOrbit* orbit, ManeuverNode* maneuver); diff --git a/src/gooey.cpp b/src/gooey.cpp index 8b3fdb9..dd0de06 100644 --- a/src/gooey.cpp +++ b/src/gooey.cpp @@ -273,15 +273,15 @@ drawManeuverWindow(GameState* gs) static float dv = 0.f; if (orbit) { - if (orbit->maneuver.active) { + if (orbit->last_maneuver) { Text("Orbital Maneuver"); Indent(); - Text("true anomaly: %f", orbit->maneuver.true_anomaly); - Text("impulse dv: %f km/s", orbit->maneuver.impulse_delta_v); + Text("true anomaly: %f", orbit->first_maneuver->true_anomaly); + Text("impulse dv: %f km/s", orbit->first_maneuver->impulse_delta_v); Unindent(); if (Button("remove")) { - removeManeuver(orbit); + removeManeuver(orbit, orbit->first_maneuver); item_current_idx = 0; anom = 0.f; dv = 0.f; @@ -307,7 +307,7 @@ drawManeuverWindow(GameState* gs) InputFloat("impulse dv, km/s", &dv, 0.1, 4.0, "%.1f"); if (Button("apply")) { - if (!addManeuver(orbit, ImpulseType::PROGRADE, anom, dv)) { + if (!addManeuver(gs, orbit, ImpulseType::PROGRADE, anom, dv)) { // flash a color or something? } } diff --git a/src/main.cpp b/src/main.cpp index ec10266..2c51e5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -258,7 +258,12 @@ loadScene(GameState* gs, RenderState* rs) //GameOrbit* go_2 = loadOrbit(gs, rs, body, el2, rg, "sat_02"); double v_transfer = orbitGetTransferVelocity(go_1->system, el2); - addManeuver(go_1, ImpulseType::PROGRADE, DEG2RAD(180), v_transfer); + addManeuver(gs, go_1, ImpulseType::PROGRADE, DEG2RAD(180), v_transfer); + // FIXME: can't get circularization here unless we simulate the transfer + // orbit ahead of time, eg) applyManeuver to a dummy orbit + // could also add a ImpulseType::CIRCULARIZE and calculate in + // applyManeuver() + addManeuver(gs, go_1, ImpulseType::PROGRADE, DEG2RAD(180), 0.500); return true; } @@ -301,12 +306,13 @@ updateOrbit(GameOrbit* orbit, bool running, double time_step) updateSatelliteModel(orbit->system, time_step); setEntityPosition(orbit->satellite_entity, orbit->system.sat.position); - if (testManeuverStep(orbit->maneuver, + if (orbit->first_maneuver + && testManeuverStep(orbit->first_maneuver, previous_true_anom, orbit->system.sat.theta)) { - applyManeuver(orbit, previous_true_anom); - removeManeuver(orbit); + applyManeuver(orbit, orbit->first_maneuver); + removeManeuver(orbit, orbit->first_maneuver); } } } @@ -333,6 +339,7 @@ postFrameCallback(RenderState* rs, void* user_data = nullptr) #define DEFAULT_SIM_SPEED 500 #define GAME_ARENA_SIZE 16 * 1024 * 1024 // 16MB #define DEFAULT_MAX_ORBITS 10000 +#define DEFAULT_MANUEVER_NODES 3 * DEFAULT_MAX_ORBITS int main() { @@ -355,6 +362,9 @@ main() gs.sim_speed = DEFAULT_SIM_SPEED; gs.max_orbits = DEFAULT_MAX_ORBITS; gs.orbits = ARENA_ALLOC(gs.arena, GameOrbit, DEFAULT_MAX_ORBITS); + gs.max_maneuvers = DEFAULT_MANUEVER_NODES; + gs.maneuver_nodes = + ARENA_ALLOC(gs.arena, ManeuverNode, DEFAULT_MANUEVER_NODES); if (!loadScene(&gs, rs)) { LOGF(Error, "Error loading scene\n");