Browse Source

Convert spacecraft and maneuver markers to screen-space rendering

Replace 3D billboard rendering with 2D screen-space overlays using
GetWorldToScreen() and DrawTexturePro() for constant visibility.

Changes:
- Rename render functions to *_screen_space suffix
- Use GetWorldToScreen() to transform 3D positions to screen coordinates
- Replace DrawBillboard/DrawBillboardPro() with DrawTexturePro() for 2D rendering
- Use constant pixel sizes (40px spacecraft, 20-60px markers)
- Add off-screen culling to skip rendering outside viewport
- Update render order: 3D scene first, then 2D overlays
- Update documentation to reflect screen-space approach

Benefits:
- Markers remain visible at all zoom levels (constant screen size)
- Simplified sizing with pixel values instead of world-space math
- Better performance with off-screen culling
- Clearer separation: 3D scene vs UI indicators
main
cinnaboot 6 months ago
parent
commit
38f0e0bad7
  1. 22
      docs/rendering.md
  2. 84
      src/renderer.cpp
  3. 4
      src/renderer.h

22
docs/rendering.md

@ -103,18 +103,20 @@ float scale_radius(double radius, double scale) {
### Spacecraft
**Rendering:**
- Fixed-size wireframe spheres (5.0 units)
- Screen-space 2D overlay using `DrawTexturePro()`
- Constant screen size: 40 pixels (independent of camera zoom)
- Cyan color (0, 255, 255)
- Position scaled and transformed
- Rotated to face velocity direction
- Off-screen culling (skips rendering if outside viewport)
**Purpose:** Visual marker for spacecraft tracking at solar system distances
**Purpose:** UI indicator for spacecraft that remains visible at all zoom levels
### Maneuver Markers
**Rendering:**
- Cubes (`DrawCube`) colored by burn direction
- Size based on delta-v magnitude (2-10 units, clamped)
- Screen-space 2D overlay using `DrawTexturePro()`
- Size based on delta-v magnitude (20-60 pixels, clamped)
- Only shown for unexecuted maneuvers
- Positioned at spacecraft location
- Off-screen culling (skips rendering if outside viewport)
**Color Coding:**
- PROGRADE: Green (0, 255, 0)
@ -125,7 +127,7 @@ float scale_radius(double radius, double scale) {
- RADIAL_OUT: Cyan (0, 255, 255)
- CUSTOM: White (255, 255, 255)
**Render Order:** Top layer, after spacecraft
**Render Order:** Top layer, after spacecraft (rendered as 2D overlays after 3D scene)
## Orbit Rendering
@ -270,9 +272,9 @@ float scale_radius(double radius, double scale) {
#### Rendering
- `render_body(body, render_state)` - Draw single celestial body
- `render_spacecraft(craft, render_state)` - Draw spacecraft marker
- `render_maneuver_marker(craft, maneuver, render_state)` - Draw burn indicator
- `render_simulation(sim, render_state)` - Full scene rendering (3D only)
- `render_spacecraft_screen_space(craft, render_state)` - Draw spacecraft marker as 2D overlay
- `render_maneuver_marker_screen_space(craft, maneuver, render_state)` - Draw burn indicator as 2D overlay
- `render_simulation(sim, render_state)` - Full scene rendering (3D + 2D overlays)
#### Scaling
- `scale_position(pos, scale)` - Transform simulation to render position

84
src/renderer.cpp

@ -268,36 +268,58 @@ void render_body(CelestialBody* body, RenderState* render_state) {
DrawSphereWires(position, radius, 16, 16, color);
}
void render_spacecraft(Spacecraft* craft, RenderState* render_state) {
void render_spacecraft_screen_space(Spacecraft* craft, RenderState* render_state) {
Vector3 render_pos = sim_to_render(craft->position, render_state->distance_scale);
Vector2 screen_pos = GetWorldToScreen(render_pos, render_state->camera);
int screen_width = GetScreenWidth();
int screen_height = GetScreenHeight();
if (screen_pos.x < -50 || screen_pos.x > screen_width + 50 ||
screen_pos.y < -50 || screen_pos.y > screen_height + 50) {
return;
}
ensure_textures_loaded(render_state);
Vector3 position = sim_to_render(craft->position, render_state->distance_scale);
float billboard_size = 20.0f;
float screen_size = 40.0f;
Color spacecraft_color = { 0, 255, 255, 255 };
float angle = calculate_spacecraft_angle(craft->local_velocity, render_state->camera);
DrawBillboardPro(render_state->camera,
render_state->spacecraft_texture,
(Rectangle){0, 0, 64, 64},
position,
(Vector3){0, 1, 0},
(Vector2){billboard_size, billboard_size},
(Vector2){billboard_size/2, billboard_size/2},
angle,
spacecraft_color);
Rectangle source = {0, 0, 64, 64};
Rectangle dest = {
screen_pos.x - screen_size/2,
screen_pos.y - screen_size/2,
screen_size,
screen_size
};
Vector2 origin = {screen_size/2, screen_size/2};
DrawTexturePro(render_state->spacecraft_texture,
source, dest, origin, angle, spacecraft_color);
}
void render_maneuver_marker(Spacecraft* craft, Maneuver* maneuver, RenderState* render_state) {
void render_maneuver_marker_screen_space(Spacecraft* craft, Maneuver* maneuver, RenderState* render_state) {
if (maneuver->executed) {
return;
}
Vector3 render_pos = sim_to_render(craft->position, render_state->distance_scale);
Vector2 screen_pos = GetWorldToScreen(render_pos, render_state->camera);
int screen_width = GetScreenWidth();
int screen_height = GetScreenHeight();
if (screen_pos.x < -50 || screen_pos.x > screen_width + 50 ||
screen_pos.y < -50 || screen_pos.y > screen_height + 50) {
return;
}
ensure_textures_loaded(render_state);
Vector3 position = sim_to_render(craft->position, render_state->distance_scale);
float marker_size = (float)(maneuver->delta_v / 100.0f);
if (marker_size < 4.0f) marker_size = 4.0f;
if (marker_size > 12.0f) marker_size = 12.0f;
float screen_size = (float)(maneuver->delta_v / 50.0f);
if (screen_size < 20.0f) screen_size = 20.0f;
if (screen_size > 60.0f) screen_size = 60.0f;
int texture_index;
switch (maneuver->direction) {
@ -324,11 +346,17 @@ void render_maneuver_marker(Spacecraft* craft, Maneuver* maneuver, RenderState*
break;
}
DrawBillboard(render_state->camera,
render_state->maneuver_textures[texture_index],
position,
marker_size,
WHITE);
Rectangle source = {0, 0, 32, 32};
Rectangle dest = {
screen_pos.x - screen_size/2,
screen_pos.y - screen_size/2,
screen_size,
screen_size
};
Vector2 origin = {screen_size/2, screen_size/2};
DrawTexturePro(render_state->maneuver_textures[texture_index],
source, dest, origin, 0, WHITE);
}
struct OrbitalBasis {
@ -533,21 +561,21 @@ void render_simulation(SimulationState* sim, RenderState* render_state) {
render_body(&sim->bodies[i], render_state);
}
// Render all spacecraft (after bodies for visibility)
EndMode3D();
// Render spacecraft as screen-space overlays
for (int i = 0; i < sim->craft_count; i++) {
render_spacecraft(&sim->spacecraft[i], render_state);
render_spacecraft_screen_space(&sim->spacecraft[i], render_state);
}
// Render maneuver markers (top layer)
// Render maneuver markers as screen-space overlays
for (int i = 0; i < sim->maneuver_count; i++) {
Maneuver* maneuver = &sim->maneuvers[i];
if (!maneuver->executed && maneuver->craft_index >= 0 && maneuver->craft_index < sim->craft_count) {
Spacecraft* craft = &sim->spacecraft[maneuver->craft_index];
render_maneuver_marker(craft, maneuver, render_state);
render_maneuver_marker_screen_space(craft, maneuver, render_state);
}
}
EndMode3D();
EndDrawing();
}

4
src/renderer.h

@ -43,8 +43,8 @@ void update_camera(RenderState* render_state, SimulationState* sim);
// Rendering functions
void render_body(CelestialBody* body, RenderState* render_state);
void render_spacecraft(Spacecraft* craft, RenderState* render_state);
void render_maneuver_marker(Spacecraft* craft, Maneuver* maneuver, RenderState* render_state);
void render_spacecraft_screen_space(Spacecraft* craft, RenderState* render_state);
void render_maneuver_marker_screen_space(Spacecraft* craft, Maneuver* maneuver, RenderState* render_state);
void render_simulation(SimulationState* sim, RenderState* render_state);
// Scaling functions

Loading…
Cancel
Save