Browse Source

Add dynamic orbital rendering with hyperbolic/parabolic support

- Calculate orbital elements dynamically from current state and parent
- Orbits now update automatically when parent body changes
- Add support for parabolic trajectories (e ≈ 1)
- Add support for hyperbolic trajectories (e > 1) with limited extent
- Properly orient orbits in 3D space using eccentricity vector
- Limit hyperbolic/parabolic drawing to avoid infinite vertices

Refactored for code reuse and separation of concerns:
- calculate_orbital_basis(): Computes orbital reference frame
- orbital_to_cartesian(): Transforms orbital coords to 3D space
- draw_orbit_segment(): Handles rendering of individual segments
- render_elliptical_orbit(): Draws closed elliptical paths
- render_hyperbolic_orbit(): Draws open hyperbolic/parabolic paths

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
main
cinnaboot 6 months ago
parent
commit
060ff40323
  1. 138
      src/renderer.cpp

138
src/renderer.cpp

@ -102,23 +102,115 @@ void render_body(CelestialBody* body, RenderState* render_state) {
DrawSphereWires(position, radius, 16, 16, color);
}
struct OrbitalBasis {
Vec3 periapsis_dir;
Vec3 normal;
Vec3 q_vec;
};
static OrbitalBasis calculate_orbital_basis(Vec3 r_vec, Vec3 velocity, Vec3 e_vec) {
OrbitalBasis basis;
basis.periapsis_dir = vec3_normalize(e_vec);
Vec3 h_vec = {
r_vec.y * velocity.z - r_vec.z * velocity.y,
r_vec.z * velocity.x - r_vec.x * velocity.z,
r_vec.x * velocity.y - r_vec.y * velocity.x
};
basis.normal = vec3_normalize(h_vec);
basis.q_vec = {
basis.normal.y * basis.periapsis_dir.z - basis.normal.z * basis.periapsis_dir.y,
basis.normal.z * basis.periapsis_dir.x - basis.normal.x * basis.periapsis_dir.z,
basis.normal.x * basis.periapsis_dir.y - basis.normal.y * basis.periapsis_dir.x
};
return basis;
}
static Vec3 orbital_to_cartesian(double x, double y, OrbitalBasis basis, Vec3 parent_pos) {
return {
basis.periapsis_dir.x * x + basis.q_vec.x * y + parent_pos.x,
basis.periapsis_dir.y * x + basis.q_vec.y * y + parent_pos.y,
basis.periapsis_dir.z * x + basis.q_vec.z * y + parent_pos.z
};
}
static void draw_orbit_segment(double x1, double y1, double x2, double y2,
OrbitalBasis basis, Vec3 parent_pos,
RenderState* render_state, Color color) {
Vec3 p1_sim = orbital_to_cartesian(x1, y1, basis, parent_pos);
Vec3 p2_sim = orbital_to_cartesian(x2, y2, basis, parent_pos);
Vector3 p1 = sim_to_render(p1_sim, render_state->distance_scale);
Vector3 p2 = sim_to_render(p2_sim, render_state->distance_scale);
DrawLine3D(p1, p2, color);
}
static void render_elliptical_orbit(double a, double e, OrbitalBasis basis,
Vec3 parent_pos, RenderState* render_state, Color color) {
double b = a * sqrt(1.0 - e * e);
double c = a * e;
int segments = 100;
for (int i = 0; i < segments; i++) {
float theta1 = (float)i / segments * 2.0f * PI;
float theta2 = (float)(i + 1) / segments * 2.0f * PI;
double x1 = a * cos(theta1) - c;
double y1 = b * sin(theta1);
double x2 = a * cos(theta2) - c;
double y2 = b * sin(theta2);
draw_orbit_segment(x1, y1, x2, y2, basis, parent_pos, render_state, color);
}
}
static void render_hyperbolic_orbit(double p, double e, OrbitalBasis basis,
Vec3 parent_pos, RenderState* render_state, Color color) {
double max_true_anomaly = (e > 1.01) ? acos(-1.0 / e) * 0.95 : PI * 0.48;
int segments = 60;
for (int i = 0; i < segments; i++) {
float theta1 = -max_true_anomaly + (float)i / segments * 2.0f * max_true_anomaly;
float theta2 = -max_true_anomaly + (float)(i + 1) / segments * 2.0f * max_true_anomaly;
double r1 = p / (1.0 + e * cos(theta1));
double r2 = p / (1.0 + e * cos(theta2));
double x1 = r1 * cos(theta1);
double y1 = r1 * sin(theta1);
double x2 = r2 * cos(theta2);
double y2 = r2 * sin(theta2);
draw_orbit_segment(x1, y1, x2, y2, basis, parent_pos, render_state, color);
}
}
// Render orbit path for a body
void render_orbit(CelestialBody* body, CelestialBody* parent, RenderState* render_state) {
if (body->parent_index == -1 || parent == NULL) {
return;
}
double a = body->semi_major_axis;
double e = body->eccentricity;
Vec3 r_vec = vec3_sub(body->position, parent->position);
double r = vec3_magnitude(r_vec);
double v = vec3_magnitude(body->velocity);
if (a <= 0.0) {
return;
}
if (r < 1.0) return;
double b = a * sqrt(1.0 - e * e);
double c = a * e;
double mu = G * parent->mass;
double specific_energy = (v * v) / 2.0 - mu / r;
Vector3 parent_pos = sim_to_render(parent->position, render_state->distance_scale);
double v_squared = v * v;
double r_dot_v = r_vec.x * body->velocity.x + r_vec.y * body->velocity.y + r_vec.z * body->velocity.z;
Vec3 e_vec = {
(v_squared - mu / r) * r_vec.x - r_dot_v * body->velocity.x,
(v_squared - mu / r) * r_vec.y - r_dot_v * body->velocity.y,
(v_squared - mu / r) * r_vec.z - r_dot_v * body->velocity.z
};
double e = vec3_magnitude(e_vec) / mu;
Color orbit_color = {
(unsigned char)(body->color[0] * 128),
@ -127,28 +219,20 @@ void render_orbit(CelestialBody* body, CelestialBody* parent, RenderState* rende
128
};
int segments = 100;
for (int i = 0; i < segments; i++) {
float theta1 = (float)i / segments * 2.0f * PI;
float theta2 = (float)(i + 1) / segments * 2.0f * PI;
OrbitalBasis basis = calculate_orbital_basis(r_vec, body->velocity, e_vec);
float x1 = (float)(a * cos(theta1) - c);
float y1 = (float)(b * sin(theta1));
float x2 = (float)(a * cos(theta2) - c);
float y2 = (float)(b * sin(theta2));
if (e < 0.98) {
double a = -mu / (2.0 * specific_energy);
if (a <= 0.0) return;
Vector3 p1 = {
parent_pos.x + x1 * (float)render_state->distance_scale,
parent_pos.y,
parent_pos.z + y1 * (float)render_state->distance_scale
};
Vector3 p2 = {
parent_pos.x + x2 * (float)render_state->distance_scale,
parent_pos.y,
parent_pos.z + y2 * (float)render_state->distance_scale
};
render_elliptical_orbit(a, e, basis, parent->position, render_state, orbit_color);
} else {
double a = (e > 1.01) ? mu / (2.0 * (-specific_energy)) : r / (1.0 + e);
double p = a * (1.0 - e * e);
if (p <= 0.0) return;
DrawLine3D(p1, p2, orbit_color);
render_hyperbolic_orbit(p, e, basis, parent->position, render_state, orbit_color);
}
}

Loading…
Cancel
Save