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

#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);
}