# include "selfdrive/ui/paint.h"
# include <cassert>
# ifdef __APPLE__
# include <OpenGL/gl3.h>
# define NANOVG_GL3_IMPLEMENTATION
# define nvgCreate nvgCreateGL3
# else
# include <GLES3/gl3.h>
# define NANOVG_GLES3_IMPLEMENTATION
# define nvgCreate nvgCreateGLES3
# endif
# define NANOVG_GLES3_IMPLEMENTATION
# include <nanovg_gl.h>
# include <nanovg_gl_utils.h>
# include "selfdrive/hardware/hw.h"
static void draw_chevron ( UIState * s , float x , float y , float sz , NVGcolor fillColor , NVGcolor glowColor ) {
// glow
float g_xo = sz / 5 ;
float g_yo = sz / 10 ;
nvgBeginPath ( s - > vg ) ;
nvgMoveTo ( s - > vg , x + ( sz * 1.35 ) + g_xo , y + sz + g_yo ) ;
nvgLineTo ( s - > vg , x , y - g_xo ) ;
nvgLineTo ( s - > vg , x - ( sz * 1.35 ) - g_xo , y + sz + g_yo ) ;
nvgClosePath ( s - > vg ) ;
nvgFillColor ( s - > vg , glowColor ) ;
nvgFill ( s - > vg ) ;
// chevron
nvgBeginPath ( s - > vg ) ;
nvgMoveTo ( s - > vg , x + ( sz * 1.25 ) , y + sz ) ;
nvgLineTo ( s - > vg , x , y ) ;
nvgLineTo ( s - > vg , x - ( sz * 1.25 ) , y + sz ) ;
nvgClosePath ( s - > vg ) ;
nvgFillColor ( s - > vg , fillColor ) ;
nvgFill ( s - > vg ) ;
}
static void draw_lead ( UIState * s , const cereal : : RadarState : : LeadData : : Reader & lead_data , const vertex_data & vd ) {
// Draw lead car indicator
auto [ x , y ] = vd ;
float fillAlpha = 0 ;
float speedBuff = 10. ;
float leadBuff = 40. ;
float d_rel = lead_data . getDRel ( ) ;
float v_rel = lead_data . getVRel ( ) ;
if ( d_rel < leadBuff ) {
fillAlpha = 255 * ( 1.0 - ( d_rel / leadBuff ) ) ;
if ( v_rel < 0 ) {
fillAlpha + = 255 * ( - 1 * ( v_rel / speedBuff ) ) ;
}
fillAlpha = ( int ) ( fmin ( fillAlpha , 255 ) ) ;
}
float sz = std : : clamp ( ( 25 * 30 ) / ( d_rel / 3 + 30 ) , 15.0f , 30.0f ) * 2.35 ;
x = std : : clamp ( x , 0.f , s - > fb_w - sz / 2 ) ;
y = std : : fmin ( s - > fb_h - sz * .6 , y ) ;
draw_chevron ( s , x , y , sz , nvgRGBA ( 201 , 34 , 49 , fillAlpha ) , COLOR_YELLOW ) ;
}
static void ui_draw_line ( UIState * s , const line_vertices_data & vd , NVGcolor * color , NVGpaint * paint ) {
if ( vd . cnt = = 0 ) return ;
const vertex_data * v = & vd . v [ 0 ] ;
nvgBeginPath ( s - > vg ) ;
nvgMoveTo ( s - > vg , v [ 0 ] . x , v [ 0 ] . y ) ;
for ( int i = 1 ; i < vd . cnt ; i + + ) {
nvgLineTo ( s - > vg , v [ i ] . x , v [ i ] . y ) ;
}
nvgClosePath ( s - > vg ) ;
if ( color ) {
nvgFillColor ( s - > vg , * color ) ;
} else if ( paint ) {
nvgFillPaint ( s - > vg , * paint ) ;
}
nvgFill ( s - > vg ) ;
}
static void ui_draw_vision_lane_lines ( UIState * s ) {
const UIScene & scene = s - > scene ;
NVGpaint track_bg ;
if ( ! scene . end_to_end ) {
// paint lanelines
for ( int i = 0 ; i < std : : size ( scene . lane_line_vertices ) ; i + + ) {
NVGcolor color = nvgRGBAf ( 1.0 , 1.0 , 1.0 , scene . lane_line_probs [ i ] ) ;
ui_draw_line ( s , scene . lane_line_vertices [ i ] , & color , nullptr ) ;
}
// paint road edges
for ( int i = 0 ; i < std : : size ( scene . road_edge_vertices ) ; i + + ) {
NVGcolor color = nvgRGBAf ( 1.0 , 0.0 , 0.0 , std : : clamp < float > ( 1.0 - scene . road_edge_stds [ i ] , 0.0 , 1.0 ) ) ;
ui_draw_line ( s , scene . road_edge_vertices [ i ] , & color , nullptr ) ;
}
track_bg = nvgLinearGradient ( s - > vg , s - > fb_w , s - > fb_h , s - > fb_w , s - > fb_h * .4 ,
COLOR_WHITE , COLOR_WHITE_ALPHA ( 0 ) ) ;
} else {
track_bg = nvgLinearGradient ( s - > vg , s - > fb_w , s - > fb_h , s - > fb_w , s - > fb_h * .4 ,
COLOR_RED , COLOR_RED_ALPHA ( 0 ) ) ;
}
// paint path
ui_draw_line ( s , scene . track_vertices , nullptr , & track_bg ) ;
}
// Draw all world space objects.
static void ui_draw_world ( UIState * s ) {
nvgScissor ( s - > vg , 0 , 0 , s - > fb_w , s - > fb_h ) ;
// Draw lane edges and vision/mpc tracks
ui_draw_vision_lane_lines ( s ) ;
// Draw lead indicators if openpilot is handling longitudinal
if ( s - > scene . longitudinal_control ) {
auto lead_one = ( * s - > sm ) [ " radarState " ] . getRadarState ( ) . getLeadOne ( ) ;
auto lead_two = ( * s - > sm ) [ " radarState " ] . getRadarState ( ) . getLeadTwo ( ) ;
if ( lead_one . getStatus ( ) ) {
draw_lead ( s , lead_one , s - > scene . lead_vertices [ 0 ] ) ;
}
if ( lead_two . getStatus ( ) & & ( std : : abs ( lead_one . getDRel ( ) - lead_two . getDRel ( ) ) > 3.0 ) ) {
draw_lead ( s , lead_two , s - > scene . lead_vertices [ 1 ] ) ;
}
}
nvgResetScissor ( s - > vg ) ;
}
static void ui_draw_vision_header ( UIState * s ) {
NVGpaint gradient = nvgLinearGradient ( s - > vg , 0 , header_h - ( header_h / 2.5 ) , 0 , header_h ,
nvgRGBAf ( 0 , 0 , 0 , 0.45 ) , nvgRGBAf ( 0 , 0 , 0 , 0 ) ) ;
nvgBeginPath ( s - > vg ) ;
nvgRect ( s - > vg , 0 , 0 , s - > fb_w , header_h ) ;
nvgFillPaint ( s - > vg , gradient ) ;
nvgFill ( s - > vg ) ;
}
static void ui_draw_vision ( UIState * s ) {
const UIScene * scene = & s - > scene ;
// Draw augmented elements
if ( scene - > world_objects_visible ) {
ui_draw_world ( s ) ;
}
// TODO: move this to Qt
ui_draw_vision_header ( s ) ;
}
void ui_draw ( UIState * s , int w , int h ) {
// Update intrinsics matrix after possible wide camera toggle change
if ( s - > fb_w ! = w | | s - > fb_h ! = h ) {
ui_resize ( s , w , h ) ;
}
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
nvgBeginFrame ( s - > vg , s - > fb_w , s - > fb_h , 1.0f ) ;
ui_draw_vision ( s ) ;
nvgEndFrame ( s - > vg ) ;
glDisable ( GL_BLEND ) ;
}
void ui_nvg_init ( UIState * s ) {
// on EON, we enable MSAA
s - > vg = Hardware : : EON ( ) ? nvgCreate ( 0 ) : nvgCreate ( NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG ) ;
assert ( s - > vg ) ;
}
void ui_resize ( UIState * s , int width , int height ) {
s - > fb_w = width ;
s - > fb_h = height ;
auto intrinsic_matrix = s - > wide_camera ? ecam_intrinsic_matrix : fcam_intrinsic_matrix ;
float zoom = ZOOM / intrinsic_matrix . v [ 0 ] ;
if ( s - > wide_camera ) {
zoom * = 0.5 ;
}
// Apply transformation such that video pixel coordinates match video
// 1) Put (0, 0) in the middle of the video
// 2) Apply same scaling as video
// 3) Put (0, 0) in top left corner of video
s - > car_space_transform . reset ( ) ;
s - > car_space_transform . translate ( width / 2 , height / 2 + y_offset )
. scale ( zoom , zoom )
. translate ( - intrinsic_matrix . v [ 2 ] , - intrinsic_matrix . v [ 5 ] ) ;
}