|
11 | 11 | #include <stdlib.h> |
12 | 12 |
|
13 | 13 | #define EARTH_RADIUS 6371000.0 |
14 | | -#define TRAIL_MAX 3600 |
| 14 | +#define TRAIL_MAX 1800 |
15 | 15 | #define TRAIL_INTERVAL 0.016f |
16 | 16 | #define TRAIL_DIST_INTERVAL 0.01f // meters between ribbon samples |
17 | 17 |
|
@@ -208,6 +208,27 @@ static Color heat_to_color(float heat, unsigned char alpha, view_mode_t mode) { |
208 | 208 | float s = (heat - 0.83f) / 0.17f; |
209 | 209 | cr = 255; cg = 250 + 5 * s; cb = 60 + 195 * s; |
210 | 210 | } |
| 211 | + } else if (mode == VIEW_SNOW) { |
| 212 | + // Snow: deep navy → royal blue → teal → green → yellow → red |
| 213 | + if (heat < 0.16f) { |
| 214 | + float s = heat / 0.16f; |
| 215 | + cr = 10 + 10 * s; cg = 20 + 20 * s; cb = 100 + 40 * s; |
| 216 | + } else if (heat < 0.33f) { |
| 217 | + float s = (heat - 0.16f) / 0.17f; |
| 218 | + cr = 20 - 10 * s; cg = 40 + 80 * s; cb = 140 + 40 * s; |
| 219 | + } else if (heat < 0.5f) { |
| 220 | + float s = (heat - 0.33f) / 0.17f; |
| 221 | + cr = 10 - 10 * s; cg = 120 + 40 * s; cb = 180 - 100 * s; |
| 222 | + } else if (heat < 0.66f) { |
| 223 | + float s = (heat - 0.5f) / 0.16f; |
| 224 | + cr = 0 + 60 * s; cg = 160 + 40 * s; cb = 80 - 80 * s; |
| 225 | + } else if (heat < 0.83f) { |
| 226 | + float s = (heat - 0.66f) / 0.17f; |
| 227 | + cr = 60 + 180 * s; cg = 200 + 20 * s; cb = 0; |
| 228 | + } else { |
| 229 | + float s = (heat - 0.83f) / 0.17f; |
| 230 | + cr = 240; cg = 220 - 180 * s; cb = 0; |
| 231 | + } |
211 | 232 | } else { |
212 | 233 | // Grid (default): purple → magenta → red → orange → yellow → white |
213 | 234 | if (heat < 0.16f) { |
@@ -351,18 +372,39 @@ void vehicle_update(vehicle_t *v, const hil_state_t *state, const home_position_ |
351 | 372 | v->airspeed = state->ind_airspeed * 0.01f; |
352 | 373 | v->altitude_rel = (float)(alt - v->alt0); |
353 | 374 |
|
354 | | - // Sample trail by time OR distance (whichever triggers first) |
| 375 | + // Adaptive trail sampling: record a point when direction changes (tight turns |
| 376 | + // get dense coverage) or after a max distance on straight runs (so they don't |
| 377 | + // go bare). Minimum distance gate prevents duplicate points when hovering. |
355 | 378 | float dist_since = 0.0f; |
| 379 | + Vector3 cur_dir = {0}; |
356 | 380 | if (v->trail_count > 0) { |
357 | 381 | int last = (v->trail_head - 1 + v->trail_capacity) % v->trail_capacity; |
358 | 382 | float ddx = v->position.x - v->trail[last].x; |
359 | 383 | float ddy = v->position.y - v->trail[last].y; |
360 | 384 | float ddz = v->position.z - v->trail[last].z; |
361 | 385 | dist_since = sqrtf(ddx*ddx + ddy*ddy + ddz*ddz); |
| 386 | + if (dist_since > 0.001f) { |
| 387 | + cur_dir = (Vector3){ ddx/dist_since, ddy/dist_since, ddz/dist_since }; |
| 388 | + } |
362 | 389 | } |
| 390 | + |
| 391 | + // Direction change: dot product < threshold means significant turn |
| 392 | + float dir_dot = 1.0f; |
| 393 | + if (v->trail_count > 1 && dist_since > 0.001f) { |
| 394 | + dir_dot = cur_dir.x * v->trail_last_dir.x + |
| 395 | + cur_dir.y * v->trail_last_dir.y + |
| 396 | + cur_dir.z * v->trail_last_dir.z; |
| 397 | + } |
| 398 | + |
| 399 | + // Sample triggers: dense on turns, sparse on straights |
363 | 400 | v->trail_timer += GetFrameTime(); |
364 | | - if (v->trail_timer >= TRAIL_INTERVAL || dist_since >= TRAIL_DIST_INTERVAL) { |
| 401 | + bool dir_trigger = (dir_dot < 0.995f) && (dist_since >= TRAIL_DIST_INTERVAL); |
| 402 | + bool dist_trigger = (dist_since >= 0.5f); |
| 403 | + bool time_trigger = (v->trail_timer >= TRAIL_INTERVAL * 4.0f); |
| 404 | + |
| 405 | + if (dir_trigger || dist_trigger || time_trigger) { |
365 | 406 | v->trail_timer = 0.0f; |
| 407 | + if (dist_since > 0.001f) v->trail_last_dir = cur_dir; |
366 | 408 | v->trail[v->trail_head] = v->position; |
367 | 409 | v->trail_roll[v->trail_head] = v->roll_deg; |
368 | 410 | v->trail_pitch[v->trail_head] = v->pitch_deg; |
@@ -616,7 +658,7 @@ void vehicle_draw(vehicle_t *v, view_mode_t view_mode, bool selected, |
616 | 658 | rlEnd(); |
617 | 659 | } else { |
618 | 660 | // ── Speed ribbon trail (mode 2) ── |
619 | | - // Batched triangle ribbon: single rlBegin/rlEnd instead of per-quad DrawTriangle3D. |
| 661 | + // Batched triangle ribbon with adaptive sampling (dense on turns, sparse on straights). |
620 | 662 | // Perpendicular blending (70% previous + 30% current) prevents twisting on tight turns. |
621 | 663 | float max_speed = v->trail_speed_max > 1.0f ? v->trail_speed_max : 1.0f; |
622 | 664 | float max_half_w = v->model_scale * 0.25f; |
|
0 commit comments