openpilot is an open source driver assistance system. openpilot performs the functions of Automated Lane Centering and Adaptive Cruise Control for over 200 supported car makes and models.
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.

824 lines
28 KiB

5 years ago
#include "ui.hpp"
#include <assert.h>
#include <map>
#include <cmath>
#include <iostream>
5 years ago
#include "common/util.h"
#define NANOVG_GLES3_IMPLEMENTATION
#include "nanovg_gl.h"
#include "nanovg_gl_utils.h"
extern "C"{
#include "common/glutil.h"
}
#include "paint.hpp"
#include "sidebar.hpp"
5 years ago
// TODO: this is also hardcoded in common/transformations/camera.py
// TODO: choose based on frame input size
#ifdef QCOM2
const float zoom = 1.5;
const mat3 intrinsic_matrix = (mat3){{
2648.0, 0.0, 1928.0/2,
0.0, 2648.0, 1208.0/2,
0.0, 0.0, 1.0
}};
#else
const float zoom = 2.35;
5 years ago
const mat3 intrinsic_matrix = (mat3){{
910., 0., 1164.0/2,
0., 910., 874.0/2,
5 years ago
0., 0., 1.
}};
#endif
5 years ago
// Projects a point in car to space to the corresponding point in full frame
// image space.
vec3 car_space_to_full_frame(const UIState *s, vec4 car_space_projective) {
const UIScene *scene = &s->scene;
// We'll call the car space point p.
// First project into normalized image coordinates with the extrinsics matrix.
const vec4 Ep4 = matvecmul(scene->extrinsic_matrix, car_space_projective);
// The last entry is zero because of how we store E (to use matvecmul).
const vec3 Ep = {{Ep4.v[0], Ep4.v[1], Ep4.v[2]}};
const vec3 KEp = matvecmul3(intrinsic_matrix, Ep);
// Project.
const vec3 p_image = {{KEp.v[0] / KEp.v[2], KEp.v[1] / KEp.v[2], 1.}};
return p_image;
}
// Calculate an interpolation between two numbers at a specific increment
static float lerp(float v0, float v1, float t) {
return (1 - t) * v0 + t * v1;
}
static void ui_draw_text(NVGcontext *vg, float x, float y, const char* string, float size, NVGcolor color, int font){
nvgFontFaceId(vg, font);
nvgFontSize(vg, size);
nvgFillColor(vg, color);
nvgText(vg, x, y, string, NULL);
}
5 years ago
static void draw_chevron(UIState *s, float x_in, float y_in, float sz,
NVGcolor fillColor, NVGcolor glowColor) {
const vec4 p_car_space = (vec4){{x_in, y_in, 0., 1.}};
const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space);
float x = p_full_frame.v[0];
float y = p_full_frame.v[1];
if (x < 0 || y < 0.){
return;
}
5 years ago
sz *= 30;
sz /= (x_in / 3 + 30);
if (sz > 30) sz = 30;
if (sz < 15) sz = 15;
5 years ago
// 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);
5 years ago
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);
5 years ago
nvgFillColor(s->vg, fillColor);
nvgFill(s->vg);
}
static void ui_draw_circle_image(NVGcontext *vg, float x, float y, int size, int image, NVGcolor color, float img_alpha, int img_y = 0) {
const int img_size = size * 1.5;
nvgBeginPath(vg);
nvgCircle(vg, x, y + (bdr_s * 1.5), size);
nvgFillColor(vg, color);
nvgFill(vg);
ui_draw_image(vg, x - (img_size / 2), img_y ? img_y : y - (size / 4), img_size, img_size, image, img_alpha);
}
static void ui_draw_circle_image(NVGcontext *vg, float x, float y, int size, int image, bool active) {
float bg_alpha = active ? 0.3f : 0.1f;
float img_alpha = active ? 1.0f : 0.15f;
ui_draw_circle_image(vg, x, y, size, image, nvgRGBA(0, 0, 0, (255 * bg_alpha)), img_alpha);
}
static void draw_lead(UIState *s, const cereal::RadarState::LeadData::Reader &lead){
// Draw lead car indicator
float fillAlpha = 0;
float speedBuff = 10.;
float leadBuff = 40.;
float d_rel = lead.getDRel();
float v_rel = lead.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));
}
draw_chevron(s, d_rel, lead.getYRel(), 25, nvgRGBA(201, 34, 49, fillAlpha), COLOR_YELLOW);
}
5 years ago
static void ui_draw_lane_line(UIState *s, const model_path_vertices_data *pvd, NVGcolor color) {
if (pvd->cnt == 0) return;
5 years ago
nvgBeginPath(s->vg);
nvgMoveTo(s->vg, pvd->v[0].x, pvd->v[0].y);
for (int i=1; i<pvd->cnt; i++) {
nvgLineTo(s->vg, pvd->v[i].x, pvd->v[i].y);
5 years ago
}
nvgClosePath(s->vg);
nvgFillColor(s->vg, color);
nvgFill(s->vg);
}
static void update_track_data(UIState *s, bool is_mpc, track_vertices_data *pvd) {
const UIScene *scene = &s->scene;
const float *points = scene->path_points;
5 years ago
const float *mpc_x_coords = &scene->mpc_x[0];
const float *mpc_y_coords = &scene->mpc_y[0];
float off = is_mpc?0.3:0.5;
float lead_d = scene->lead_data[0].getDRel()*2.;
5 years ago
float path_height = is_mpc?(lead_d>5.)?fmin(lead_d, 25.)-fmin(lead_d*0.35, 10.):20.
:(lead_d>0.)?fmin(lead_d, 50.)-fmin(lead_d*0.35, 10.):49.;
path_height = fmin(path_height, scene->model.getPath().getValidLen());
5 years ago
pvd->cnt = 0;
// left side up
for (int i=0; i<=path_height; i++) {
float px, py, mpx;
if (is_mpc) {
mpx = i==0?0.0:mpc_x_coords[i];
px = lerp(mpx+1.0, mpx, i/100.0);
py = mpc_y_coords[i] - off;
} else {
px = lerp(i+1.0, i, i/100.0);
py = points[i] - off;
5 years ago
}
vec4 p_car_space = (vec4){{px, py, 0., 1.}};
vec3 p_full_frame = car_space_to_full_frame(s, p_car_space);
if (p_full_frame.v[0] < 0. || p_full_frame.v[1] < 0.) {
continue;
}
pvd->v[pvd->cnt].x = p_full_frame.v[0];
pvd->v[pvd->cnt].y = p_full_frame.v[1];
pvd->cnt += 1;
}
// right side down
for (int i=path_height; i>=0; i--) {
float px, py, mpx;
if (is_mpc) {
mpx = i==0?0.0:mpc_x_coords[i];
px = lerp(mpx+1.0, mpx, i/100.0);
py = mpc_y_coords[i] + off;
} else {
px = lerp(i+1.0, i, i/100.0);
py = points[i] + off;
5 years ago
}
vec4 p_car_space = (vec4){{px, py, 0., 1.}};
vec3 p_full_frame = car_space_to_full_frame(s, p_car_space);
if (p_full_frame.v[0] < 0. || p_full_frame.v[1] < 0.) {
continue;
}
5 years ago
pvd->v[pvd->cnt].x = p_full_frame.v[0];
pvd->v[pvd->cnt].y = p_full_frame.v[1];
pvd->cnt += 1;
}
}
static void update_all_track_data(UIState *s) {
const UIScene *scene = &s->scene;
// Draw vision path
update_track_data(s, false, &s->track_vertices[0]);
if (scene->controls_state.getEnabled()) {
5 years ago
// Draw MPC path when engaged
update_track_data(s, true, &s->track_vertices[1]);
}
}
static void ui_draw_track(UIState *s, bool is_mpc, track_vertices_data *pvd) {
if (pvd->cnt == 0) return;
5 years ago
nvgBeginPath(s->vg);
nvgMoveTo(s->vg, pvd->v[0].x, pvd->v[0].y);
for (int i=1; i<pvd->cnt; i++) {
nvgLineTo(s->vg, pvd->v[i].x, pvd->v[i].y);
5 years ago
}
nvgClosePath(s->vg);
NVGpaint track_bg;
if (is_mpc) {
// Draw colored MPC track
const NVGcolor clr = bg_colors[s->status];
track_bg = nvgLinearGradient(s->vg, s->fb_w, s->fb_h, s->fb_w, s->fb_h*.4,
nvgRGBA(clr.r, clr.g, clr.b, 255), nvgRGBA(clr.r, clr.g, clr.b, 255/2));
5 years ago
} else {
// Draw white vision track
track_bg = nvgLinearGradient(s->vg, s->fb_w, s->fb_h, s->fb_w, s->fb_h*.4,
COLOR_WHITE, COLOR_WHITE_ALPHA(0));
5 years ago
}
nvgFillPaint(s->vg, track_bg);
nvgFill(s->vg);
}
static void draw_frame(UIState *s) {
mat4 *out_mat;
5 years ago
if (s->scene.frontview) {
glBindVertexArray(s->frame_vao[1]);
out_mat = &s->front_frame_mat;
} else {
glBindVertexArray(s->frame_vao[0]);
5 years ago
out_mat = &s->rear_frame_mat;
}
glActiveTexture(GL_TEXTURE0);
if (s->stream.last_idx >= 0) {
glBindTexture(GL_TEXTURE_2D, s->frame_texs[s->stream.last_idx]);
#ifndef QCOM
// this is handled in ion on QCOM
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, s->stream.bufs_info.width, s->stream.bufs_info.height,
0, GL_RGB, GL_UNSIGNED_BYTE, s->priv_hnds[s->stream.last_idx]);
#endif
5 years ago
}
glUseProgram(s->frame_program);
glUniform1i(s->frame_texture_loc, 0);
glUniformMatrix4fv(s->frame_transform_loc, 1, GL_TRUE, out_mat->v);
assert(glGetError() == GL_NO_ERROR);
glEnableVertexAttribArray(0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void*)0);
glDisableVertexAttribArray(0);
glBindVertexArray(0);
}
static inline bool valid_frame_pt(UIState *s, float x, float y) {
return x >= 0 && x <= s->stream.bufs_info.width && y >= 0 && y <= s->stream.bufs_info.height;
5 years ago
}
static void update_lane_line_data(UIState *s, const float *points, float off, model_path_vertices_data *pvd, float valid_len) {
5 years ago
pvd->cnt = 0;
int rcount = fmin(MODEL_PATH_MAX_VERTICES_CNT / 2, valid_len);
for (int i = 0; i < rcount; i++) {
5 years ago
float px = (float)i;
float py = points[i] - off;
const vec4 p_car_space = (vec4){{px, py, 0., 1.}};
const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space);
if(!valid_frame_pt(s, p_full_frame.v[0], p_full_frame.v[1]))
continue;
pvd->v[pvd->cnt].x = p_full_frame.v[0];
pvd->v[pvd->cnt].y = p_full_frame.v[1];
pvd->cnt += 1;
}
for (int i = rcount - 1; i > 0; i--) {
5 years ago
float px = (float)i;
float py = points[i] + off;
5 years ago
const vec4 p_car_space = (vec4){{px, py, 0., 1.}};
const vec3 p_full_frame = car_space_to_full_frame(s, p_car_space);
if(!valid_frame_pt(s, p_full_frame.v[0], p_full_frame.v[1]))
continue;
pvd->v[pvd->cnt].x = p_full_frame.v[0];
pvd->v[pvd->cnt].y = p_full_frame.v[1];
pvd->cnt += 1;
}
}
static void update_all_lane_lines_data(UIState *s, const cereal::ModelData::PathData::Reader &path, const float *points, model_path_vertices_data *pstart) {
update_lane_line_data(s, points, 0.025*path.getProb(), pstart, path.getValidLen());
update_lane_line_data(s, points, fmin(path.getStd(), 0.7), pstart + 1, path.getValidLen());
5 years ago
}
static void ui_draw_lane(UIState *s, model_path_vertices_data *pstart, NVGcolor color) {
5 years ago
ui_draw_lane_line(s, pstart, color);
color.a /= 25;
5 years ago
ui_draw_lane_line(s, pstart + 1, color);
}
static void ui_draw_vision_lanes(UIState *s) {
const UIScene *scene = &s->scene;
model_path_vertices_data *pvd = &s->model_path_vertices[0];
if(s->sm->updated("model")) {
update_all_lane_lines_data(s, scene->model.getLeftLane(), scene->left_lane_points, pvd);
update_all_lane_lines_data(s, scene->model.getRightLane(), scene->right_lane_points, pvd + MODEL_LANE_PATH_CNT);
5 years ago
}
5 years ago
// Draw left lane edge
ui_draw_lane(s, pvd, nvgRGBAf(1.0, 1.0, 1.0, scene->model.getLeftLane().getProb()));
5 years ago
// Draw right lane edge
ui_draw_lane(s, pvd + MODEL_LANE_PATH_CNT, nvgRGBAf(1.0, 1.0, 1.0, scene->model.getRightLane().getProb()));
5 years ago
if(s->sm->updated("radarState")) {
5 years ago
update_all_track_data(s);
}
5 years ago
// Draw vision path
ui_draw_track(s, false, &s->track_vertices[0]);
if (scene->controls_state.getEnabled()) {
5 years ago
// Draw MPC path when engaged
ui_draw_track(s, true, &s->track_vertices[1]);
}
}
// Draw all world space objects.
static void ui_draw_world(UIState *s) {
const UIScene *scene = &s->scene;
nvgSave(s->vg);
// Don't draw on top of sidebar
nvgScissor(s->vg, scene->viz_rect.x, scene->viz_rect.y, scene->viz_rect.w, scene->viz_rect.h);
// Apply transformation such that video pixel coordinates match video
// 1) Put (0, 0) in the middle of the video
nvgTranslate(s->vg, s->video_rect.x + s->video_rect.w / 2, s->video_rect.y + s->video_rect.h / 2);
// 2) Apply same scaling as video
nvgScale(s->vg, zoom, zoom);
// 3) Put (0, 0) in top left corner of video
nvgTranslate(s->vg, -intrinsic_matrix.v[2], -intrinsic_matrix.v[5]);
5 years ago
// Draw lane edges and vision/mpc tracks
ui_draw_vision_lanes(s);
// Draw lead indicators if openpilot is handling longitudinal
if (s->longitudinal_control) {
if (scene->lead_data[0].getStatus()) {
draw_lead(s, scene->lead_data[0]);
}
if (scene->lead_data[1].getStatus() && (std::abs(scene->lead_data[0].getDRel() - scene->lead_data[1].getDRel()) > 3.0)) {
draw_lead(s, scene->lead_data[1]);
}
5 years ago
}
nvgRestore(s->vg);
5 years ago
}
static void ui_draw_vision_maxspeed(UIState *s) {
char maxspeed_str[32];
float maxspeed = s->scene.controls_state.getVCruise();
5 years ago
int maxspeed_calc = maxspeed * 0.6225 + 0.5;
if (s->is_metric) {
maxspeed_calc = maxspeed + 0.5;
}
bool is_cruise_set = (maxspeed != 0 && maxspeed != SET_SPEED_NA);
int viz_maxspeed_w = 184;
int viz_maxspeed_h = 202;
int viz_maxspeed_x = s->scene.viz_rect.x + (bdr_s*2);
int viz_maxspeed_y = s->scene.viz_rect.y + (bdr_s*1.5);
5 years ago
int viz_maxspeed_xo = 180;
viz_maxspeed_xo = 0;
// Draw Background
ui_draw_rect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, COLOR_BLACK_ALPHA(100), 30);
5 years ago
// Draw Border
NVGcolor color = COLOR_WHITE_ALPHA(100);
ui_draw_rect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, color, 20, 10);
5 years ago
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
const int text_x = viz_maxspeed_x + (viz_maxspeed_xo / 2) + (viz_maxspeed_w / 2);
ui_draw_text(s->vg, text_x, 148, "MAX", 26 * 2.5, COLOR_WHITE_ALPHA(is_cruise_set ? 200 : 100), s->font_sans_regular);
5 years ago
if (is_cruise_set) {
snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", maxspeed_calc);
ui_draw_text(s->vg, text_x, 242, maxspeed_str, 48 * 2.5, COLOR_WHITE, s->font_sans_bold);
5 years ago
} else {
ui_draw_text(s->vg, text_x, 242, "N/A", 42 * 2.5, COLOR_WHITE_ALPHA(100), s->font_sans_semibold);
5 years ago
}
}
static void ui_draw_vision_speed(UIState *s) {
const Rect &viz_rect = s->scene.viz_rect;
float v_ego = s->scene.controls_state.getVEgo();
float speed = v_ego * 2.2369363 + 0.5;
if (s->is_metric){
speed = v_ego * 3.6 + 0.5;
}
5 years ago
const int viz_speed_w = 280;
const int viz_speed_x = viz_rect.centerX() - viz_speed_w/2;
5 years ago
char speed_str[32];
nvgBeginPath(s->vg);
nvgRect(s->vg, viz_speed_x, viz_rect.y, viz_speed_w, header_h);
5 years ago
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
snprintf(speed_str, sizeof(speed_str), "%d", (int)speed);
ui_draw_text(s->vg, viz_rect.centerX(), 240, speed_str, 96*2.5, COLOR_WHITE, s->font_sans_bold);
ui_draw_text(s->vg, viz_rect.centerX(), 320, s->is_metric?"km/h":"mph", 36*2.5, COLOR_WHITE_ALPHA(200), s->font_sans_regular);
5 years ago
}
static void ui_draw_vision_event(UIState *s) {
const int viz_event_w = 220;
const int viz_event_x = s->scene.viz_rect.right() - (viz_event_w + bdr_s*2);
const int viz_event_y = s->scene.viz_rect.y + (bdr_s*1.5);
if (s->scene.controls_state.getDecelForModel() && s->scene.controls_state.getEnabled()) {
5 years ago
// draw winding road sign
const int img_turn_size = 160*1.5;
ui_draw_image(s->vg, viz_event_x - (img_turn_size / 4), viz_event_y + bdr_s - 25, img_turn_size, img_turn_size, s->img_turn, 1.0f);
} else if (s->scene.controls_state.getEngageable()) {
5 years ago
// draw steering wheel
const int bg_wheel_size = 96;
const int bg_wheel_x = viz_event_x + (viz_event_w-bg_wheel_size);
const int bg_wheel_y = viz_event_y + (bg_wheel_size/2);
const NVGcolor color = bg_colors[s->status];
ui_draw_circle_image(s->vg, bg_wheel_x, bg_wheel_y, bg_wheel_size, s->img_wheel, color, 1.0f, bg_wheel_y - 25);
5 years ago
}
}
static void ui_draw_vision_face(UIState *s) {
const int face_size = 96;
const int face_x = (s->scene.viz_rect.x + face_size + (bdr_s * 2));
const int face_y = (s->scene.viz_rect.bottom() - footer_h + ((footer_h - face_size) / 2));
ui_draw_circle_image(s->vg, face_x, face_y, face_size, s->img_face, s->scene.dmonitoring_state.getFaceDetected());
5 years ago
}
static void ui_draw_driver_view(UIState *s) {
const UIScene *scene = &s->scene;
s->scene.uilayout_sidebarcollapsed = true;
const Rect &viz_rect = s->scene.viz_rect;
const int ff_xoffset = 32;
const int frame_x = viz_rect.x;
const int frame_w = viz_rect.w;
const int valid_frame_w = 4 * viz_rect.h / 3;
const int box_y = viz_rect.y;
const int box_h = viz_rect.h;
const int valid_frame_x = frame_x + (frame_w - valid_frame_w) / 2 + ff_xoffset;
// blackout
NVGpaint gradient = nvgLinearGradient(s->vg, scene->is_rhd ? valid_frame_x : (valid_frame_x + valid_frame_w),
box_y,
scene->is_rhd ? (valid_frame_w - box_h / 2) : (valid_frame_x + box_h / 2), box_y,
COLOR_BLACK, COLOR_BLACK_ALPHA(0));
ui_draw_rect(s->vg, scene->is_rhd ? valid_frame_x : (valid_frame_x + box_h / 2), box_y, valid_frame_w - box_h / 2, box_h, gradient);
ui_draw_rect(s->vg, scene->is_rhd ? valid_frame_x : valid_frame_x + box_h / 2, box_y, valid_frame_w - box_h / 2, box_h, COLOR_BLACK_ALPHA(144));
// borders
ui_draw_rect(s->vg, frame_x, box_y, valid_frame_x - frame_x, box_h, nvgRGBA(23, 51, 73, 255));
ui_draw_rect(s->vg, valid_frame_x + valid_frame_w, box_y, frame_w - valid_frame_w - (valid_frame_x - frame_x), box_h, nvgRGBA(23, 51, 73, 255));
// draw face box
if (scene->dmonitoring_state.getFaceDetected()) {
auto fxy_list = scene->driver_state.getFacePosition();
const float face_x = fxy_list[0];
const float face_y = fxy_list[1];
float fbox_x;
float fbox_y = box_y + (face_y + 0.5) * box_h - 0.5 * 0.6 * box_h / 2;;
if (!scene->is_rhd) {
fbox_x = valid_frame_x + (1 - (face_x + 0.5)) * (box_h / 2) - 0.5 * 0.6 * box_h / 2;
} else {
fbox_x = valid_frame_x + valid_frame_w - box_h / 2 + (face_x + 0.5) * (box_h / 2) - 0.5 * 0.6 * box_h / 2;
}
if (std::abs(face_x) <= 0.35 && std::abs(face_y) <= 0.4) {
ui_draw_rect(s->vg, fbox_x, fbox_y, 0.6 * box_h / 2, 0.6 * box_h / 2,
nvgRGBAf(1.0, 1.0, 1.0, 0.8 - ((std::abs(face_x) > std::abs(face_y) ? std::abs(face_x) : std::abs(face_y))) * 0.6 / 0.375),
35, 10);
} else {
ui_draw_rect(s->vg, fbox_x, fbox_y, 0.6 * box_h / 2, 0.6 * box_h / 2, nvgRGBAf(1.0, 1.0, 1.0, 0.2), 35, 10);
}
}
// draw face icon
const int face_size = 85;
const int x = (valid_frame_x + face_size + (bdr_s * 2)) + (scene->is_rhd ? valid_frame_w - box_h / 2:0);
const int y = (box_y + box_h - face_size - bdr_s - (bdr_s * 1.5));
ui_draw_circle_image(s->vg, x, y, face_size, s->img_face, scene->dmonitoring_state.getFaceDetected());
}
5 years ago
static void ui_draw_vision_header(UIState *s) {
const Rect &viz_rect = s->scene.viz_rect;
NVGpaint gradient = nvgLinearGradient(s->vg, viz_rect.x,
viz_rect.y+(header_h-(header_h/2.5)),
viz_rect.x, viz_rect.y+header_h,
5 years ago
nvgRGBAf(0,0,0,0.45), nvgRGBAf(0,0,0,0));
ui_draw_rect(s->vg, viz_rect.x, viz_rect.y, viz_rect.w, header_h, gradient);
5 years ago
ui_draw_vision_maxspeed(s);
ui_draw_vision_speed(s);
ui_draw_vision_event(s);
}
static void ui_draw_vision_footer(UIState *s) {
ui_draw_vision_face(s);
}
void ui_draw_vision_alert(UIState *s, cereal::ControlsState::AlertSize va_size, UIStatus va_color,
5 years ago
const char* va_text1, const char* va_text2) {
static std::map<cereal::ControlsState::AlertSize, const int> alert_size_map = {
{cereal::ControlsState::AlertSize::NONE, 0},
{cereal::ControlsState::AlertSize::SMALL, 241},
{cereal::ControlsState::AlertSize::MID, 390},
{cereal::ControlsState::AlertSize::FULL, s->fb_h}};
5 years ago
const UIScene *scene = &s->scene;
bool longAlert1 = strlen(va_text1) > 15;
NVGcolor color = bg_colors[va_color];
color.a *= s->alert_blinking_alpha;
int alr_s = alert_size_map[va_size];
const int alr_x = scene->viz_rect.x - bdr_s;
const int alr_w = scene->viz_rect.w + (bdr_s*2);
const int alr_h = alr_s+(va_size==cereal::ControlsState::AlertSize::NONE?0:bdr_s);
const int alr_y = s->fb_h-alr_h;
5 years ago
ui_draw_rect(s->vg, alr_x, alr_y, alr_w, alr_h, color);
5 years ago
NVGpaint gradient = nvgLinearGradient(s->vg, alr_x, alr_y, alr_x, alr_y+alr_h,
nvgRGBAf(0.0,0.0,0.0,0.05), nvgRGBAf(0.0,0.0,0.0,0.35));
ui_draw_rect(s->vg, alr_x, alr_y, alr_w, alr_h, gradient);
5 years ago
nvgFillColor(s->vg, COLOR_WHITE);
5 years ago
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
if (va_size == cereal::ControlsState::AlertSize::SMALL) {
ui_draw_text(s->vg, alr_x+alr_w/2, alr_y+alr_h/2+15, va_text1, 40*2.5, COLOR_WHITE, s->font_sans_semibold);
} else if (va_size == cereal::ControlsState::AlertSize::MID) {
ui_draw_text(s->vg, alr_x+alr_w/2, alr_y+alr_h/2-45, va_text1, 48*2.5, COLOR_WHITE, s->font_sans_bold);
ui_draw_text(s->vg, alr_x+alr_w/2, alr_y+alr_h/2+75, va_text2, 36*2.5, COLOR_WHITE, s->font_sans_regular);
} else if (va_size == cereal::ControlsState::AlertSize::FULL) {
5 years ago
nvgFontSize(s->vg, (longAlert1?72:96)*2.5);
nvgFontFaceId(s->vg, s->font_sans_bold);
5 years ago
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
nvgTextBox(s->vg, alr_x, alr_y+(longAlert1?360:420), alr_w-60, va_text1, NULL);
nvgFontSize(s->vg, 48*2.5);
nvgFontFaceId(s->vg, s->font_sans_regular);
5 years ago
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BOTTOM);
nvgTextBox(s->vg, alr_x, alr_h-(longAlert1?300:360), alr_w-60, va_text2, NULL);
}
}
static void ui_draw_vision(UIState *s) {
const UIScene *scene = &s->scene;
const Rect &viz_rect = scene->viz_rect;
5 years ago
// Draw video frames
glEnable(GL_SCISSOR_TEST);
glViewport(s->video_rect.x, s->video_rect.y, s->video_rect.w, s->video_rect.h);
glScissor(viz_rect.x, viz_rect.y, viz_rect.w, viz_rect.h);
5 years ago
draw_frame(s);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0, s->fb_w, s->fb_h);
5 years ago
// Draw augmented elements
if (!scene->frontview && scene->world_objects_visible) {
5 years ago
ui_draw_world(s);
}
// Set Speed, Current Speed, Status/Events
if (!scene->frontview) {
ui_draw_vision_header(s);
} else {
ui_draw_driver_view(s);
}
5 years ago
if (scene->alert_size != cereal::ControlsState::AlertSize::NONE) {
ui_draw_vision_alert(s, scene->alert_size, s->status,
scene->alert_text1.c_str(), scene->alert_text2.c_str());
} else if (!scene->frontview) {
ui_draw_vision_footer(s);
5 years ago
}
}
static void ui_draw_background(UIState *s) {
const NVGcolor color = bg_colors[s->status];
glClearColor(color.r, color.g, color.b, 1.0);
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
}
5 years ago
void ui_draw(UIState *s) {
s->scene.viz_rect = Rect{bdr_s * 3, bdr_s, s->fb_w - 4 * bdr_s, s->fb_h - 2 * bdr_s};
s->scene.ui_viz_ro = 0;
if (!s->scene.uilayout_sidebarcollapsed) {
s->scene.viz_rect.x = sbr_w + bdr_s;
s->scene.viz_rect.w = s->fb_w - s->scene.viz_rect.x - bdr_s;
s->scene.ui_viz_ro = -(sbr_w - 6 * bdr_s);
}
ui_draw_background(s);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, s->fb_w, s->fb_h);
nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f);
ui_draw_sidebar(s);
if (s->started && s->active_app == cereal::UiLayoutState::App::NONE &&
s->status != STATUS_OFFROAD && s->vision_connected) {
ui_draw_vision(s);
}
nvgEndFrame(s->vg);
glDisable(GL_BLEND);
5 years ago
}
void ui_draw_image(NVGcontext *vg, float x, float y, float w, float h, int image, float alpha){
nvgBeginPath(vg);
NVGpaint imgPaint = nvgImagePattern(vg, x, y, w, h, 0, image, alpha);
nvgRect(vg, x, y, w, h);
nvgFillPaint(vg, imgPaint);
nvgFill(vg);
}
void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGcolor color, float r, int width) {
nvgBeginPath(vg);
r > 0 ? nvgRoundedRect(vg, x, y, w, h, r) : nvgRect(vg, x, y, w, h);
if (width) {
nvgStrokeColor(vg, color);
nvgStrokeWidth(vg, width);
nvgStroke(vg);
} else {
nvgFillColor(vg, color);
nvgFill(vg);
}
}
void ui_draw_rect(NVGcontext *vg, float x, float y, float w, float h, NVGpaint &paint, float r){
nvgBeginPath(vg);
r > 0? nvgRoundedRect(vg, x, y, w, h, r) : nvgRect(vg, x, y, w, h);
nvgFillPaint(vg, paint);
nvgFill(vg);
}
5 years ago
static const char frame_vertex_shader[] =
#ifdef NANOVG_GL3_IMPLEMENTATION
"#version 150 core\n"
#else
"#version 300 es\n"
#endif
"in vec4 aPosition;\n"
"in vec4 aTexCoord;\n"
5 years ago
"uniform mat4 uTransform;\n"
"out vec4 vTexCoord;\n"
5 years ago
"void main() {\n"
" gl_Position = uTransform * aPosition;\n"
" vTexCoord = aTexCoord;\n"
"}\n";
static const char frame_fragment_shader[] =
#ifdef NANOVG_GL3_IMPLEMENTATION
"#version 150 core\n"
#else
"#version 300 es\n"
#endif
5 years ago
"precision mediump float;\n"
"uniform sampler2D uTexture;\n"
"in vec4 vTexCoord;\n"
"out vec4 colorOut;\n"
5 years ago
"void main() {\n"
" colorOut = texture(uTexture, vTexCoord.xy);\n"
5 years ago
"}\n";
static const mat4 device_transform = {{
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
// frame from 4/3 to 16/9 display
static const mat4 full_to_wide_frame_transform = {{
.75, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
void ui_nvg_init(UIState *s) {
// init drawing
#ifdef QCOM
// on QCOM, we enable MSAA
s->vg = nvgCreate(0);
#else
s->vg = nvgCreate(NVG_ANTIALIAS | NVG_STENCIL_STROKES | NVG_DEBUG);
#endif
5 years ago
assert(s->vg);
s->font_sans_regular = nvgCreateFont(s->vg, "sans-regular", "../assets/fonts/opensans_regular.ttf");
assert(s->font_sans_regular >= 0);
s->font_sans_semibold = nvgCreateFont(s->vg, "sans-semibold", "../assets/fonts/opensans_semibold.ttf");
assert(s->font_sans_semibold >= 0);
s->font_sans_bold = nvgCreateFont(s->vg, "sans-bold", "../assets/fonts/opensans_bold.ttf");
assert(s->font_sans_bold >= 0);
s->img_wheel = nvgCreateImage(s->vg, "../assets/img_chffr_wheel.png", 1);
assert(s->img_wheel != 0);
5 years ago
s->img_turn = nvgCreateImage(s->vg, "../assets/img_trafficSign_turn.png", 1);
assert(s->img_turn != 0);
5 years ago
s->img_face = nvgCreateImage(s->vg, "../assets/img_driver_face.png", 1);
assert(s->img_face != 0);
s->img_button_settings = nvgCreateImage(s->vg, "../assets/images/button_settings.png", 1);
assert(s->img_button_settings != 0);
s->img_button_home = nvgCreateImage(s->vg, "../assets/images/button_home.png", 1);
assert(s->img_button_home != 0);
s->img_battery = nvgCreateImage(s->vg, "../assets/images/battery.png", 1);
assert(s->img_battery != 0);
s->img_battery_charging = nvgCreateImage(s->vg, "../assets/images/battery_charging.png", 1);
assert(s->img_battery_charging != 0);
for(int i=0;i<=5;++i) {
char network_asset[32];
snprintf(network_asset, sizeof(network_asset), "../assets/images/network_%d.png", i);
s->img_network[i] = nvgCreateImage(s->vg, network_asset, 1);
assert(s->img_network[i] != 0);
}
5 years ago
// init gl
s->frame_program = load_program(frame_vertex_shader, frame_fragment_shader);
assert(s->frame_program);
s->frame_pos_loc = glGetAttribLocation(s->frame_program, "aPosition");
s->frame_texcoord_loc = glGetAttribLocation(s->frame_program, "aTexCoord");
s->frame_texture_loc = glGetUniformLocation(s->frame_program, "uTexture");
s->frame_transform_loc = glGetUniformLocation(s->frame_program, "uTransform");
glViewport(0, 0, s->fb_w, s->fb_h);
glDisable(GL_DEPTH_TEST);
assert(glGetError() == GL_NO_ERROR);
for(int i = 0; i < 2; i++) {
float x1, x2, y1, y2;
if (i == 1) {
// flip horizontally so it looks like a mirror
x1 = 0.0;
x2 = 1.0;
y1 = 1.0;
y2 = 0.0;
} else {
x1 = 1.0;
x2 = 0.0;
y1 = 1.0;
y2 = 0.0;
}
const uint8_t frame_indicies[] = {0, 1, 2, 0, 2, 3};
const float frame_coords[4][4] = {
{-1.0, -1.0, x2, y1}, //bl
{-1.0, 1.0, x2, y2}, //tl
{ 1.0, 1.0, x1, y2}, //tr
{ 1.0, -1.0, x1, y1}, //br
};
glGenVertexArrays(1, &s->frame_vao[i]);
5 years ago
glBindVertexArray(s->frame_vao[i]);
glGenBuffers(1, &s->frame_vbo[i]);
glBindBuffer(GL_ARRAY_BUFFER, s->frame_vbo[i]);
glBufferData(GL_ARRAY_BUFFER, sizeof(frame_coords), frame_coords, GL_STATIC_DRAW);
glEnableVertexAttribArray(s->frame_pos_loc);
glVertexAttribPointer(s->frame_pos_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)0);
glEnableVertexAttribArray(s->frame_texcoord_loc);
glVertexAttribPointer(s->frame_texcoord_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)(sizeof(float) * 2));
glGenBuffers(1, &s->frame_ibo[i]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->frame_ibo[i]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(frame_indicies), frame_indicies, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);
}
s->video_rect = Rect{bdr_s * 3, bdr_s, s->fb_w - 4 * bdr_s, s->fb_h - 2 * bdr_s};
float zx = zoom * 2 * intrinsic_matrix.v[2] / s->video_rect.w;
float zy = zoom * 2 * intrinsic_matrix.v[5] / s->video_rect.h;
const mat4 frame_transform = {{
zx, 0.0, 0.0, 0.0,
0.0, zy, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
5 years ago
s->front_frame_mat = matmul(device_transform, full_to_wide_frame_transform);
s->rear_frame_mat = matmul(device_transform, frame_transform);
for(int i = 0; i < UI_BUF_COUNT; i++) {
s->khr[i] = 0;
5 years ago
s->priv_hnds[i] = NULL;
}
}