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.

573 lines
19 KiB

#include <assert.h>
#include <algorithm>
#include "ui.h"
#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
#include "common/util.h"
#include "common/timing.h"
Torch model (#2452) * refactor draw model * rebase master * correct valid_len * rename function * rename variables * white space * rebase to master * e16c13ac-927d-455e-ae0a-81b482a2c787 * start rewriting * save proress * compiles! * oops * many fixes * seems to work * fix desires * finally cleaned * wrong std for ll * dont pulse none * compiles! * ready to test * WIP does not compile * compiles * various fixes * does something! * full 3d * not needed * draw up to 100m * fix segfault * wrong sign * fix flicker * add road edges * finish v2 packet * Added pytorch supercombo * fix rebase * no more keras * Hacky solution to the NCHW/NHWC incompatibility between SNPE and our frame data * dont break dmonitoringd, final model 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 * fix hack * Revert "fix hack" This reverts commit 5550fc01a7881d065a5eddbbb42dac55ef7ec36c. * Removed axis permutation hack * Folded padding layers into conv layers * Removed the last pad layer from the dlc * Revert "Removed the last pad layer from the dlc" This reverts commit b85f24b9e1d04abf64e85901a7ff49e00d82020a. * Revert "Folded padding layers into conv layers" This reverts commit b8d1773e4e76dea481acebbfad6a6235fbb58463. * vision model: 5034ac8b-5703-4a49-948b-11c064d10880/780 temporal model: 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 with permute + pool opt * fix ui drawing with clips * ./compile_torch.py 5034ac8b-5703-4a49-948b-11c064d10880/780 dfcd2375-81d8-49df-95bf-1d2d6ad86010/450 with variable history length * std::clamp * not sure how this compiled before * 2895ace6-a296-47ac-86e6-17ea800a74e5/550 * db090195-8810-42de-ab38-bb835d775d87/601 * 5m is very little * onnx runner * add onnxruntime to pipfile * run in real time without using the whole CPU * bump cereal; * add stds * set road edge opacity based on stddev * don't access the model packet in paint * convert mat.h to a c++ header file (#2499) * update tests * safety first Co-authored-by: deanlee <deanlee3@gmail.com> Co-authored-by: mitchell <mitchell@comma.ai> Co-authored-by: Comma Device <device@comma.ai> Co-authored-by: George Hotz <george@comma.ai> Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> old-commit-hash: 08846b5c0ec3a5299c8cb73904da15a18d3aaccf
5 years ago
#define NANOVG_GLES3_IMPLEMENTATION
#include "nanovg_gl.h"
#include "nanovg_gl_utils.h"
#include "paint.h"
// TODO: this is also hardcoded in common/transformations/camera.py
// TODO: choose based on frame input size
#ifdef QCOM2
const float y_offset = 150.0;
const float zoom = 2912.8;
#else
const float y_offset = 0.0;
const float zoom = 2138.5;
#endif
static void ui_draw_text(const UIState *s, float x, float y, const char *string, float size, NVGcolor color, const char *font_name) {
nvgFontFace(s->vg, font_name);
nvgFontSize(s->vg, size);
nvgFillColor(s->vg, color);
nvgText(s->vg, x, y, string, NULL);
}
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 ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, NVGcolor color, float img_alpha) {
nvgBeginPath(s->vg);
nvgCircle(s->vg, center_x, center_y, radius);
nvgFillColor(s->vg, color);
nvgFill(s->vg);
const int img_size = radius * 1.5;
ui_draw_image(s, {center_x - (img_size / 2), center_y - (img_size / 2), img_size, img_size}, image, img_alpha);
}
static void ui_draw_circle_image(const UIState *s, int center_x, int center_y, int radius, const char *image, bool active) {
float bg_alpha = active ? 0.3f : 0.1f;
float img_alpha = active ? 1.0f : 0.15f;
ui_draw_circle_image(s, center_x, center_y, radius, image, nvgRGBA(0, 0, 0, (255 * bg_alpha)), img_alpha);
}
static void draw_lead(UIState *s, int idx) {
// Draw lead car indicator
const auto &lead = s->scene.lead_data[idx];
auto [x, y] = s->scene.lead_vertices[idx];
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));
}
float sz = std::clamp((25 * 30) / (d_rel / 3 + 30), 15.0f, 30.0f) * s->zoom;
x = std::clamp(x, 0.f, s->viz_rect.right() - sz / 2);
y = std::fmin(s->viz_rect.bottom() - 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);
Torch model (#2452) * refactor draw model * rebase master * correct valid_len * rename function * rename variables * white space * rebase to master * e16c13ac-927d-455e-ae0a-81b482a2c787 * start rewriting * save proress * compiles! * oops * many fixes * seems to work * fix desires * finally cleaned * wrong std for ll * dont pulse none * compiles! * ready to test * WIP does not compile * compiles * various fixes * does something! * full 3d * not needed * draw up to 100m * fix segfault * wrong sign * fix flicker * add road edges * finish v2 packet * Added pytorch supercombo * fix rebase * no more keras * Hacky solution to the NCHW/NHWC incompatibility between SNPE and our frame data * dont break dmonitoringd, final model 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 * fix hack * Revert "fix hack" This reverts commit 5550fc01a7881d065a5eddbbb42dac55ef7ec36c. * Removed axis permutation hack * Folded padding layers into conv layers * Removed the last pad layer from the dlc * Revert "Removed the last pad layer from the dlc" This reverts commit b85f24b9e1d04abf64e85901a7ff49e00d82020a. * Revert "Folded padding layers into conv layers" This reverts commit b8d1773e4e76dea481acebbfad6a6235fbb58463. * vision model: 5034ac8b-5703-4a49-948b-11c064d10880/780 temporal model: 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 with permute + pool opt * fix ui drawing with clips * ./compile_torch.py 5034ac8b-5703-4a49-948b-11c064d10880/780 dfcd2375-81d8-49df-95bf-1d2d6ad86010/450 with variable history length * std::clamp * not sure how this compiled before * 2895ace6-a296-47ac-86e6-17ea800a74e5/550 * db090195-8810-42de-ab38-bb835d775d87/601 * 5m is very little * onnx runner * add onnxruntime to pipfile * run in real time without using the whole CPU * bump cereal; * add stds * set road edge opacity based on stddev * don't access the model packet in paint * convert mat.h to a c++ header file (#2499) * update tests * safety first Co-authored-by: deanlee <deanlee3@gmail.com> Co-authored-by: mitchell <mitchell@comma.ai> Co-authored-by: Comma Device <device@comma.ai> Co-authored-by: George Hotz <george@comma.ai> Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> old-commit-hash: 08846b5c0ec3a5299c8cb73904da15a18d3aaccf
5 years ago
if (color) {
nvgFillColor(s->vg, *color);
} else if (paint) {
nvgFillPaint(s->vg, *paint);
}
nvgFill(s->vg);
}
static void draw_frame(UIState *s) {
mat4 *out_mat;
if (s->scene.driver_view) {
glBindVertexArray(s->frame_vao[1]);
out_mat = &s->front_frame_mat;
} else {
glBindVertexArray(s->frame_vao[0]);
out_mat = &s->rear_frame_mat;
}
glActiveTexture(GL_TEXTURE0);
if (s->last_frame) {
glBindTexture(GL_TEXTURE_2D, s->texture[s->last_frame->idx]->frame_tex);
#ifndef QCOM
// this is handled in ion on QCOM
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, s->last_frame->width, s->last_frame->height,
0, GL_RGB, GL_UNSIGNED_BYTE, s->last_frame->addr);
#endif
}
glUseProgram(s->gl_shader->prog);
glUniform1i(s->gl_shader->getUniformLocation("uTexture"), 0);
glUniformMatrix4fv(s->gl_shader->getUniformLocation("uTransform"), 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);
}
Torch model (#2452) * refactor draw model * rebase master * correct valid_len * rename function * rename variables * white space * rebase to master * e16c13ac-927d-455e-ae0a-81b482a2c787 * start rewriting * save proress * compiles! * oops * many fixes * seems to work * fix desires * finally cleaned * wrong std for ll * dont pulse none * compiles! * ready to test * WIP does not compile * compiles * various fixes * does something! * full 3d * not needed * draw up to 100m * fix segfault * wrong sign * fix flicker * add road edges * finish v2 packet * Added pytorch supercombo * fix rebase * no more keras * Hacky solution to the NCHW/NHWC incompatibility between SNPE and our frame data * dont break dmonitoringd, final model 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 * fix hack * Revert "fix hack" This reverts commit 5550fc01a7881d065a5eddbbb42dac55ef7ec36c. * Removed axis permutation hack * Folded padding layers into conv layers * Removed the last pad layer from the dlc * Revert "Removed the last pad layer from the dlc" This reverts commit b85f24b9e1d04abf64e85901a7ff49e00d82020a. * Revert "Folded padding layers into conv layers" This reverts commit b8d1773e4e76dea481acebbfad6a6235fbb58463. * vision model: 5034ac8b-5703-4a49-948b-11c064d10880/780 temporal model: 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 with permute + pool opt * fix ui drawing with clips * ./compile_torch.py 5034ac8b-5703-4a49-948b-11c064d10880/780 dfcd2375-81d8-49df-95bf-1d2d6ad86010/450 with variable history length * std::clamp * not sure how this compiled before * 2895ace6-a296-47ac-86e6-17ea800a74e5/550 * db090195-8810-42de-ab38-bb835d775d87/601 * 5m is very little * onnx runner * add onnxruntime to pipfile * run in real time without using the whole CPU * bump cereal; * add stds * set road edge opacity based on stddev * don't access the model packet in paint * convert mat.h to a c++ header file (#2499) * update tests * safety first Co-authored-by: deanlee <deanlee3@gmail.com> Co-authored-by: mitchell <mitchell@comma.ai> Co-authored-by: Comma Device <device@comma.ai> Co-authored-by: George Hotz <george@comma.ai> Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> old-commit-hash: 08846b5c0ec3a5299c8cb73904da15a18d3aaccf
5 years ago
static void ui_draw_vision_lane_lines(UIState *s) {
UI: refactor model related functions (#19657) Squashed commit of the following: commit 7d7fb15e4cdd0e47fa38f949f79581dc611db015 Author: Willem Melching <willem.melching@gmail.com> Date: Wed Jan 6 11:31:09 2021 +0100 no extern commit 901893966e3f908435bceb8777253dfa916a3d91 Merge: 9816e01e c0703a57 Author: Willem Melching <willem.melching@gmail.com> Date: Wed Jan 6 11:27:29 2021 +0100 Merge branch 'master' into refactor_model_draw commit 9816e01e6ff0284521fa18b07ec15b3900577687 Author: deanlee <deanlee3@gmail.com> Date: Wed Jan 6 04:02:08 2021 +0000 fix lead_d commit 6fa500dbce26544cc3fdeb4d9b0d2d654382a40b Author: deanlee <deanlee3@gmail.com> Date: Tue Jan 5 13:18:20 2021 +0000 populate model data in update_socket commit e25db7e28c1b543a04c3e837aa44431c42dbb5b2 Author: deanlee <deanlee3@gmail.com> Date: Tue Jan 5 12:59:13 2021 +0000 update model in function commit 0834a3ffe7eaa080671f13c32dd6475ae5392d5b Author: deanlee <deanlee3@gmail.com> Date: Tue Jan 5 12:50:21 2021 +0000 continue commit 95b08679c2593eb0770a68b3cf1a59f1fb3b74f4 Author: Willem Melching <willem.melching@gmail.com> Date: Tue Jan 5 13:26:34 2021 +0100 Update selfdrive/ui/paint.cc commit 0eef065ae4f96c2ee5d97b4ccdfdbf0ae4945385 Author: Dean Lee <deanlee3@gmail.com> Date: Tue Jan 5 20:11:01 2021 +0800 Update selfdrive/ui/paint.cc Co-authored-by: Willem Melching <willem.melching@gmail.com> commit 955ad9b03a176174b86b28d5bb6f48d6e2a09281 Author: deanlee <deanlee3@gmail.com> Date: Thu Dec 31 04:45:31 2020 +0000 refactor model related functions old-commit-hash: 37cb4bec631197f6b369bf89f94786772bd05336
4 years ago
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));
}
Torch model (#2452) * refactor draw model * rebase master * correct valid_len * rename function * rename variables * white space * rebase to master * e16c13ac-927d-455e-ae0a-81b482a2c787 * start rewriting * save proress * compiles! * oops * many fixes * seems to work * fix desires * finally cleaned * wrong std for ll * dont pulse none * compiles! * ready to test * WIP does not compile * compiles * various fixes * does something! * full 3d * not needed * draw up to 100m * fix segfault * wrong sign * fix flicker * add road edges * finish v2 packet * Added pytorch supercombo * fix rebase * no more keras * Hacky solution to the NCHW/NHWC incompatibility between SNPE and our frame data * dont break dmonitoringd, final model 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 * fix hack * Revert "fix hack" This reverts commit 5550fc01a7881d065a5eddbbb42dac55ef7ec36c. * Removed axis permutation hack * Folded padding layers into conv layers * Removed the last pad layer from the dlc * Revert "Removed the last pad layer from the dlc" This reverts commit b85f24b9e1d04abf64e85901a7ff49e00d82020a. * Revert "Folded padding layers into conv layers" This reverts commit b8d1773e4e76dea481acebbfad6a6235fbb58463. * vision model: 5034ac8b-5703-4a49-948b-11c064d10880/780 temporal model: 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 with permute + pool opt * fix ui drawing with clips * ./compile_torch.py 5034ac8b-5703-4a49-948b-11c064d10880/780 dfcd2375-81d8-49df-95bf-1d2d6ad86010/450 with variable history length * std::clamp * not sure how this compiled before * 2895ace6-a296-47ac-86e6-17ea800a74e5/550 * db090195-8810-42de-ab38-bb835d775d87/601 * 5m is very little * onnx runner * add onnxruntime to pipfile * run in real time without using the whole CPU * bump cereal; * add stds * set road edge opacity based on stddev * don't access the model packet in paint * convert mat.h to a c++ header file (#2499) * update tests * safety first Co-authored-by: deanlee <deanlee3@gmail.com> Co-authored-by: mitchell <mitchell@comma.ai> Co-authored-by: Comma Device <device@comma.ai> Co-authored-by: George Hotz <george@comma.ai> Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> old-commit-hash: 08846b5c0ec3a5299c8cb73904da15a18d3aaccf
5 years ago
// paint path
ui_draw_line(s, scene.track_vertices, nullptr, &track_bg);
}
// Draw all world space objects.
static void ui_draw_world(UIState *s) {
const UIScene *scene = &s->scene;
// Don't draw on top of sidebar
nvgScissor(s->vg, s->viz_rect.x, s->viz_rect.y, s->viz_rect.w, s->viz_rect.h);
// Draw lane edges and vision/mpc tracks
Torch model (#2452) * refactor draw model * rebase master * correct valid_len * rename function * rename variables * white space * rebase to master * e16c13ac-927d-455e-ae0a-81b482a2c787 * start rewriting * save proress * compiles! * oops * many fixes * seems to work * fix desires * finally cleaned * wrong std for ll * dont pulse none * compiles! * ready to test * WIP does not compile * compiles * various fixes * does something! * full 3d * not needed * draw up to 100m * fix segfault * wrong sign * fix flicker * add road edges * finish v2 packet * Added pytorch supercombo * fix rebase * no more keras * Hacky solution to the NCHW/NHWC incompatibility between SNPE and our frame data * dont break dmonitoringd, final model 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 * fix hack * Revert "fix hack" This reverts commit 5550fc01a7881d065a5eddbbb42dac55ef7ec36c. * Removed axis permutation hack * Folded padding layers into conv layers * Removed the last pad layer from the dlc * Revert "Removed the last pad layer from the dlc" This reverts commit b85f24b9e1d04abf64e85901a7ff49e00d82020a. * Revert "Folded padding layers into conv layers" This reverts commit b8d1773e4e76dea481acebbfad6a6235fbb58463. * vision model: 5034ac8b-5703-4a49-948b-11c064d10880/780 temporal model: 229e3ce1-7259-412b-85e6-cc646d70f1d8/430 with permute + pool opt * fix ui drawing with clips * ./compile_torch.py 5034ac8b-5703-4a49-948b-11c064d10880/780 dfcd2375-81d8-49df-95bf-1d2d6ad86010/450 with variable history length * std::clamp * not sure how this compiled before * 2895ace6-a296-47ac-86e6-17ea800a74e5/550 * db090195-8810-42de-ab38-bb835d775d87/601 * 5m is very little * onnx runner * add onnxruntime to pipfile * run in real time without using the whole CPU * bump cereal; * add stds * set road edge opacity based on stddev * don't access the model packet in paint * convert mat.h to a c++ header file (#2499) * update tests * safety first Co-authored-by: deanlee <deanlee3@gmail.com> Co-authored-by: mitchell <mitchell@comma.ai> Co-authored-by: Comma Device <device@comma.ai> Co-authored-by: George Hotz <george@comma.ai> Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> old-commit-hash: 08846b5c0ec3a5299c8cb73904da15a18d3aaccf
5 years ago
ui_draw_vision_lane_lines(s);
// Draw lead indicators if openpilot is handling longitudinal
if (s->scene.longitudinal_control) {
if (scene->lead_data[0].getStatus()) {
draw_lead(s, 0);
}
if (scene->lead_data[1].getStatus() && (std::abs(scene->lead_data[0].getDRel() - scene->lead_data[1].getDRel()) > 3.0)) {
draw_lead(s, 1);
}
}
nvgResetScissor(s->vg);
}
static void ui_draw_vision_maxspeed(UIState *s) {
const int SET_SPEED_NA = 255;
float maxspeed = s->scene.controls_state.getVCruise();
const bool is_cruise_set = maxspeed != 0 && maxspeed != SET_SPEED_NA;
if (is_cruise_set && !s->scene.is_metric) { maxspeed *= 0.6225; }
const Rect rect = {s->viz_rect.x + (bdr_s * 2), int(s->viz_rect.y + (bdr_s * 1.5)), 184, 202};
ui_fill_rect(s->vg, rect, COLOR_BLACK_ALPHA(100), 30.);
ui_draw_rect(s->vg, rect, COLOR_WHITE_ALPHA(100), 10, 20.);
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
ui_draw_text(s, rect.centerX(), 148, "MAX", 26 * 2.5, COLOR_WHITE_ALPHA(is_cruise_set ? 200 : 100), "sans-regular");
if (is_cruise_set) {
const std::string maxspeed_str = std::to_string((int)std::nearbyint(maxspeed));
ui_draw_text(s, rect.centerX(), 242, maxspeed_str.c_str(), 48 * 2.5, COLOR_WHITE, "sans-bold");
} else {
ui_draw_text(s, rect.centerX(), 242, "N/A", 42 * 2.5, COLOR_WHITE_ALPHA(100), "sans-semibold");
}
}
static void ui_draw_vision_speed(UIState *s) {
const float speed = std::max(0.0, s->scene.car_state.getVEgo() * (s->scene.is_metric ? 3.6 : 2.2369363));
const std::string speed_str = std::to_string((int)std::nearbyint(speed));
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
ui_draw_text(s, s->viz_rect.centerX(), 240, speed_str.c_str(), 96 * 2.5, COLOR_WHITE, "sans-bold");
ui_draw_text(s, s->viz_rect.centerX(), 320, s->scene.is_metric ? "km/h" : "mph", 36 * 2.5, COLOR_WHITE_ALPHA(200), "sans-regular");
}
static void ui_draw_vision_event(UIState *s) {
if (s->scene.controls_state.getEngageable()) {
// draw steering wheel
const int radius = 96;
const int center_x = s->viz_rect.right() - radius - bdr_s * 2;
const int center_y = s->viz_rect.y + radius + (bdr_s * 1.5);
ui_draw_circle_image(s, center_x, center_y, radius, "wheel", bg_colors[s->status], 1.0f);
}
}
static void ui_draw_vision_face(UIState *s) {
const int radius = 96;
const int center_x = s->viz_rect.x + radius + (bdr_s * 2);
const int center_y = s->viz_rect.bottom() - footer_h / 2;
ui_draw_circle_image(s, center_x, center_y, radius, "driver_face", s->scene.dmonitoring_state.getIsActiveMode());
}
static void ui_draw_driver_view(UIState *s) {
const bool is_rhd = s->scene.is_rhd;
const int width = 4 * s->viz_rect.h / 3;
const Rect rect = {s->viz_rect.centerX() - width / 2, s->viz_rect.y, width, s->viz_rect.h}; // x, y, w, h
const Rect valid_rect = {is_rhd ? rect.right() - rect.h / 2 : rect.x, rect.y, rect.h / 2, rect.h};
// blackout
const int blackout_x_r = valid_rect.right();
#ifndef QCOM2
const int blackout_w_r = rect.right() - valid_rect.right();
const int blackout_x_l = rect.x;
#else
const int blackout_w_r = s->viz_rect.right() - valid_rect.right();
const int blackout_x_l = s->viz_rect.x;
#endif
const int blackout_w_l = valid_rect.x - blackout_x_l;
ui_fill_rect(s->vg, {blackout_x_l, rect.y, blackout_w_l, rect.h}, COLOR_BLACK_ALPHA(144));
ui_fill_rect(s->vg, {blackout_x_r, rect.y, blackout_w_r, rect.h}, COLOR_BLACK_ALPHA(144));
const bool face_detected = s->scene.driver_state.getFaceProb() > 0.4;
if (face_detected) {
auto fxy_list = s->scene.driver_state.getFacePosition();
float face_x = fxy_list[0];
float face_y = fxy_list[1];
int fbox_x = valid_rect.centerX() + (is_rhd ? face_x : -face_x) * valid_rect.w;
int fbox_y = valid_rect.centerY() + face_y * valid_rect.h;
float alpha = 0.2;
if (face_x = std::abs(face_x), face_y = std::abs(face_y); face_x <= 0.35 && face_y <= 0.4)
alpha = 0.8 - (face_x > face_y ? face_x : face_y) * 0.6 / 0.375;
const int box_size = 0.6 * rect.h / 2;
ui_draw_rect(s->vg, {fbox_x - box_size / 2, fbox_y - box_size / 2, box_size, box_size}, nvgRGBAf(1.0, 1.0, 1.0, alpha), 10, 35.);
}
// draw face icon
const int face_radius = 85;
const int center_x = is_rhd ? rect.right() - face_radius - bdr_s * 2 : rect.x + face_radius + bdr_s * 2;
const int center_y = rect.bottom() - face_radius - bdr_s * 2.5;
ui_draw_circle_image(s, center_x, center_y, face_radius, "driver_face", face_detected);
}
static void ui_draw_vision_header(UIState *s) {
NVGpaint gradient = nvgLinearGradient(s->vg, s->viz_rect.x,
s->viz_rect.y+(header_h-(header_h/2.5)),
s->viz_rect.x, s->viz_rect.y+header_h,
nvgRGBAf(0,0,0,0.45), nvgRGBAf(0,0,0,0));
ui_fill_rect(s->vg, {s->viz_rect.x, s->viz_rect.y, s->viz_rect.w, header_h}, gradient);
ui_draw_vision_maxspeed(s);
ui_draw_vision_speed(s);
ui_draw_vision_event(s);
}
static void ui_draw_vision_frame(UIState *s) {
// 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(s->viz_rect.x, s->viz_rect.y, s->viz_rect.w, s->viz_rect.h);
draw_frame(s);
glDisable(GL_SCISSOR_TEST);
glViewport(0, 0, s->fb_w, s->fb_h);
}
static void ui_draw_vision(UIState *s) {
const UIScene *scene = &s->scene;
if (!scene->driver_view) {
// Draw augmented elements
if (scene->world_objects_visible) {
ui_draw_world(s);
}
// Set Speed, Current Speed, Status/Events
ui_draw_vision_header(s);
if (s->scene.controls_state.getAlertSize() == cereal::ControlsState::AlertSize::NONE) {
ui_draw_vision_face(s);
}
} else {
ui_draw_driver_view(s);
}
}
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);
}
void ui_draw(UIState *s, int w, int h) {
s->viz_rect = Rect{bdr_s, bdr_s, w - 2 * bdr_s, h - 2 * bdr_s};
const bool draw_vision = s->scene.started && s->vipc_client->connected;
// GL drawing functions
ui_draw_background(s);
if (draw_vision) {
ui_draw_vision_frame(s);
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glViewport(0, 0, s->fb_w, s->fb_h);
// NVG drawing functions - should be no GL inside NVG frame
nvgBeginFrame(s->vg, s->fb_w, s->fb_h, 1.0f);
if (draw_vision) {
ui_draw_vision(s);
}
if (s->scene.driver_view && !s->vipc_client->connected) {
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
ui_draw_text(s, s->viz_rect.centerX(), s->viz_rect.centerY(), "Please wait for camera to start", 40 * 2.5, COLOR_WHITE, "sans-bold");
}
nvgEndFrame(s->vg);
glDisable(GL_BLEND);
}
void ui_draw_image(const UIState *s, const Rect &r, const char *name, float alpha) {
nvgBeginPath(s->vg);
NVGpaint imgPaint = nvgImagePattern(s->vg, r.x, r.y, r.w, r.h, 0, s->images.at(name), alpha);
nvgRect(s->vg, r.x, r.y, r.w, r.h);
nvgFillPaint(s->vg, imgPaint);
nvgFill(s->vg);
}
void ui_draw_rect(NVGcontext *vg, const Rect &r, NVGcolor color, int width, float radius) {
nvgBeginPath(vg);
radius > 0 ? nvgRoundedRect(vg, r.x, r.y, r.w, r.h, radius) : nvgRect(vg, r.x, r.y, r.w, r.h);
nvgStrokeColor(vg, color);
nvgStrokeWidth(vg, width);
nvgStroke(vg);
}
static inline void fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor *color, const NVGpaint *paint, float radius) {
nvgBeginPath(vg);
radius > 0 ? nvgRoundedRect(vg, r.x, r.y, r.w, r.h, radius) : nvgRect(vg, r.x, r.y, r.w, r.h);
if (color) nvgFillColor(vg, *color);
if (paint) nvgFillPaint(vg, *paint);
nvgFill(vg);
}
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGcolor &color, float radius) {
fill_rect(vg, r, &color, nullptr, radius);
}
void ui_fill_rect(NVGcontext *vg, const Rect &r, const NVGpaint &paint, float radius) {
fill_rect(vg, r, nullptr, &paint, radius);
}
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"
"uniform mat4 uTransform;\n"
"out vec4 vTexCoord;\n"
"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
"precision mediump float;\n"
"uniform sampler2D uTexture;\n"
"in vec4 vTexCoord;\n"
"out vec4 colorOut;\n"
"void main() {\n"
" colorOut = texture(uTexture, vTexCoord.xy);\n"
#ifdef QCOM
" vec3 dz = vec3(0.0627f, 0.0627f, 0.0627f);\n"
" colorOut.rgb = ((vec3(1.0f, 1.0f, 1.0f) - dz) * colorOut.rgb / vec3(1.0f, 1.0f, 1.0f)) + dz;\n"
#endif
"}\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,
}};
static const float driver_view_ratio = 1.333;
#ifndef QCOM2
// frame from 4/3 to 16/9 display
static const mat4 driver_view_transform = {{
driver_view_ratio*(1080-2*bdr_s)/(1920-2*bdr_s), 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,
}};
#else
// from dmonitoring.cc
static const int full_width_tici = 1928;
static const int full_height_tici = 1208;
static const int adapt_width_tici = 668;
static const int crop_x_offset = 32;
static const int crop_y_offset = -196;
static const float yscale = full_height_tici * driver_view_ratio / adapt_width_tici;
static const float xscale = yscale*(1080-2*bdr_s)/(2160-2*bdr_s)*full_width_tici/full_height_tici;
static const mat4 driver_view_transform = {{
xscale, 0.0, 0.0, xscale*crop_x_offset/full_width_tici*2,
0.0, yscale, 0.0, yscale*crop_y_offset/full_height_tici*2,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
#endif
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
assert(s->vg);
// init fonts
std::pair<const char *, const char *> fonts[] = {
{"sans-regular", "../assets/fonts/opensans_regular.ttf"},
{"sans-semibold", "../assets/fonts/opensans_semibold.ttf"},
{"sans-bold", "../assets/fonts/opensans_bold.ttf"},
};
for (auto [name, file] : fonts) {
int font_id = nvgCreateFont(s->vg, name, file);
assert(font_id >= 0);
}
// init images
std::vector<std::pair<const char *, const char *>> images = {
{"wheel", "../assets/img_chffr_wheel.png"},
{"driver_face", "../assets/img_driver_face.png"},
};
for (auto [name, file] : images) {
s->images[name] = nvgCreateImage(s->vg, file, 1);
assert(s->images[name] != 0);
}
// init gl
s->gl_shader = std::make_unique<GLShader>(frame_vertex_shader, frame_fragment_shader);
GLint frame_pos_loc = glGetAttribLocation(s->gl_shader->prog, "aPosition");
GLint frame_texcoord_loc = glGetAttribLocation(s->gl_shader->prog, "aTexCoord");
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]);
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(frame_pos_loc);
glVertexAttribPointer(frame_pos_loc, 2, GL_FLOAT, GL_FALSE,
sizeof(frame_coords[0]), (const void *)0);
glEnableVertexAttribArray(frame_texcoord_loc);
glVertexAttribPointer(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);
}
auto intrinsic_matrix = s->wide_camera ? ecam_intrinsic_matrix : fcam_intrinsic_matrix;
s->zoom = zoom / intrinsic_matrix.v[0];
if (s->wide_camera) {
s->zoom *= 0.5;
}
s->video_rect = Rect{bdr_s, bdr_s, s->fb_w - 2 * bdr_s, s->fb_h - 2 * bdr_s};
float zx = s->zoom * 2 * intrinsic_matrix.v[2] / s->video_rect.w;
float zy = s->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, -y_offset / s->video_rect.h * 2,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
}};
s->front_frame_mat = matmul(device_transform, driver_view_transform);
s->rear_frame_mat = matmul(device_transform, frame_transform);
// 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 + y_offset);
// 2) Apply same scaling as video
nvgScale(s->vg, s->zoom, s->zoom);
// 3) Put (0, 0) in top left corner of video
nvgTranslate(s->vg, -intrinsic_matrix.v[2], -intrinsic_matrix.v[5]);
nvgCurrentTransform(s->vg, s->car_space_transform);
nvgResetTransform(s->vg);
}