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.
296 lines
7.3 KiB
296 lines
7.3 KiB
|
|
#include "game.h" |
|
|
|
|
|
// forware declarations |
|
|
|
void changeOrbitColor(GameOrbit* orbit, vec3* color_data); |
|
|
|
|
|
// interface |
|
|
|
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; |
|
} |
|
|
|
GameOrbit* |
|
getSelectedOrbit(GameState* gs) |
|
{ |
|
GameOrbit* orbit = nullptr; |
|
|
|
for (u32 i = 0; i < gs->num_orbits; i++) { |
|
if (gs->orbits[i].selected) |
|
orbit = &gs->orbits[i]; |
|
} |
|
|
|
return orbit; |
|
} |
|
|
|
void |
|
disableGameOrbit(GameState* gs, GameOrbit* orbit) |
|
{ |
|
if (gs->last_selected_orbit == orbit) |
|
gs->last_selected_orbit = nullptr; |
|
|
|
*orbit = {0}; |
|
} |
|
|
|
double |
|
getTimeStep(GameState* gs) |
|
{ |
|
u64 last_sdl_tick = gs->game_time_ms; |
|
gs->game_time_ms = SDL_GetTicks64(); |
|
u64 current_tick = gs->game_time_ms - last_sdl_tick; |
|
double time_step = 0; |
|
|
|
// 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; |
|
time_step = double(diff_ms) / 1000; |
|
} |
|
|
|
return time_step; |
|
} |
|
|
|
Ellipse3D |
|
ellipse3DInit(dmat3 rotation, EllipseParameters ep, uint vert_count) |
|
{ |
|
assert(ep.a > 0 && ep.b > 0 && |
|
ep.a >= ep.b && |
|
vert_count > 0); |
|
|
|
Ellipse3D e3d = { nullptr, vert_count}; |
|
// FIXME: should be allocated from GameState->arena |
|
e3d.vertices = UTIL_ALLOC(vert_count, vec3); |
|
ellipse3DUpdate(rotation, ep, e3d); |
|
return e3d; |
|
} |
|
|
|
void |
|
ellipse3DUpdate(dmat3 rotation, EllipseParameters ep, Ellipse3D& e3d) |
|
{ |
|
double angle = 2 * M_PI / e3d.vert_count; |
|
|
|
for (uint i = 0; i < e3d.vert_count; i++) { |
|
double a = angle * i; |
|
// FIXME: we should have a function for this... |
|
// NOTE: solving for distance in polar coordinates relative to focus |
|
double r = ep.a * (1 - pow(ep.e, 2)) / (1 + ep.e * cos(a)); |
|
e3d.vertices[i] = rotation * vec3(polarToRect(a, r), 0); |
|
} |
|
} |
|
|
|
void |
|
selectOrbit(GameState* gs, GameOrbit* orbit) |
|
{ |
|
assert(orbit && orbit->ellipse_entity); |
|
|
|
// FIXME: need to allocate these somewhere other than stack |
|
static vec3 selected_colors[DEFAULT_ORBIT_VERTICES]; |
|
|
|
for (u32 i = 0; i < DEFAULT_ORBIT_VERTICES; i++) |
|
selected_colors[i] = SELECTED_ELLIPSE_COLOR; |
|
|
|
static vec3 default_colors[DEFAULT_ORBIT_VERTICES]; |
|
|
|
for (u32 i = 0; i < DEFAULT_ORBIT_VERTICES; i++) |
|
default_colors[i] = DEFAULT_ELLIPSE_COLOR; |
|
|
|
if (gs->last_selected_orbit != nullptr) { |
|
changeOrbitColor(gs->last_selected_orbit, default_colors); |
|
gs->last_selected_orbit->selected = false; |
|
} |
|
|
|
changeOrbitColor(orbit, selected_colors); |
|
orbit->selected = true; |
|
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(GameState* gs, |
|
GameOrbit* orbit, |
|
ImpulseType impulse_type, |
|
double true_anomaly, |
|
double impulse_delta_v) |
|
{ |
|
if (true_anomaly < -M_PI || true_anomaly > M_PI) { |
|
LOGF(Error, "invalid true true_anomaly: %f\n", true_anomaly); |
|
return false; |
|
} |
|
|
|
if (impulse_delta_v == 0.f) { |
|
LOGF(Warning, "refusing to add maneuver with 0 dv\n"); |
|
return false; |
|
} |
|
|
|
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; |
|
|
|
return true; |
|
} |
|
|
|
LOGF(Warning, "failed to add maneuver\n"); |
|
assert(0); |
|
return false; |
|
} |
|
|
|
// NOTE: test if the maneuver node would have occured between 2 orbit positions |
|
// eg) between 2 time steps |
|
bool |
|
testManeuverStep(ManeuverNode* maneuver, |
|
double previous_theta, |
|
double next_theta) |
|
{ |
|
assert(maneuver); |
|
|
|
// NOTE: clamp angles between 0 and 2 pi to simplify inequalities |
|
double r = orbitClampAngle(maneuver->true_anomaly); |
|
double clamped_previous_theta = orbitClampAngle(previous_theta); |
|
double clamped_next_theta = orbitClampAngle(next_theta); |
|
|
|
// FIXME: unhandled cases: |
|
// 2) direction is ccw |
|
|
|
// NOTE: sat passess through 0 |
|
if (clamped_next_theta - clamped_previous_theta < 0) { |
|
if (r >= clamped_previous_theta || r <= clamped_next_theta) |
|
return true; |
|
} |
|
|
|
else if (r >= clamped_previous_theta && r <= clamped_next_theta) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
void |
|
applyManeuver(GameOrbit* orbit, ManeuverNode* maneuver) |
|
{ |
|
TwoBodySystem& sys = orbit->system; |
|
|
|
assert(maneuver); |
|
// FIXME: assuming prograde impulse vector |
|
assert(maneuver->impulse_type == ImpulseType::PROGRADE); |
|
|
|
// re-calculate state vectors at the maneuver node |
|
double theta = maneuver->true_anomaly; |
|
double mu = sys.body.mu; |
|
double r = orbitGetRadialDistance(sys.ep.e, sys.ep.p, theta); |
|
|
|
dvec3 pos = orbitGetPositionVector(r, theta); |
|
dvec3 vel = orbitGetVelocityVector(mu, sys.h, sys.elements.e, theta); |
|
sys.rotation = orbitGetXForm(sys.elements); |
|
pos = sys.rotation * pos; |
|
vel = sys.rotation * vel; |
|
|
|
// apply impulse along velocity vector |
|
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); |
|
|
|
// update satellite true anamoly |
|
sys.sat.theta = sys.elements.nu; |
|
|
|
// update ellipse3D & GLBuffer vertices |
|
ellipse3DUpdate(sys.rotation, sys.ep, orbit->e3d); |
|
GLBuffer* buf = &orbit->ellipse_entity->meshes[0].vertex_attrib_buffers[0]; |
|
assert(utilCStrMatch(buf->name, "position")); |
|
assert(buf->data_size == orbit->e3d.vert_count * sizeof(vec3)); |
|
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 |
|
|
|
void |
|
changeOrbitColor(GameOrbit* orbit, vec3* color_data) |
|
{ |
|
GLBuffer* color_buf = nullptr; |
|
GLMesh* gl_mesh = &orbit->ellipse_entity->meshes[0]; |
|
|
|
for (u32 i = 0; i < gl_mesh->num_vertex_attrib_buffers; i ++) { |
|
if (utilCStrMatch(gl_mesh->vertex_attrib_buffers[i].name, "color")) |
|
color_buf = &gl_mesh->vertex_attrib_buffers[i]; |
|
} |
|
|
|
assert(color_buf && color_buf->data_size == orbit->e3d.vert_count * 3 * 4); |
|
updateGLBuffer(color_buf, color_data); |
|
} |
|
|
|
|