opengl_plot/module.jai
2025-02-28 18:07:43 +01:00

819 lines
25 KiB
Plaintext

PlotSettings :: struct {
colors : PlotColors;
fixed_bounds : bool;
top_left : Vector2;
bottom_right : Vector2;
}
PLOT_STYLE :: enum s32 {
TICK_MARKS;
SCALE_MARKER;
}
PLOT_DATA_STYLE :: enum {
ONCE;
EVERY_FRAME;
}
draw_plot :: (key: string, size: Vector2, xdata: []float64, yarrays: [][]float64, using settings: PlotSettings, data_style := PLOT_DATA_STYLE.ONCE) {
plot, success := table_find(*plots, key);
if !success {
plot = New(Plot);
init_plot(plot, key, xx size.x, xx size.y);
table_set(*plots, key, plot);
if data_style == .ONCE {
plot.xfloat = NewArray(xdata.count, float);
plot.xmin = 0.0;
plot.xmax = 0.0;
x_acc : float;
for xdata {
plot.xfloat[it_index] = cast(float) it;
plot.xfloat[it_index] = cast(float) it;
if it > plot.xmax plot.xmax = xx it;
if it < plot.xmin plot.xmin = xx it;
is_nan, is_inf := is_nan_is_inf(cast(float) it);
if !is_nan && !is_inf {
x_acc += cast(float) it;
}
}
plot.x_avg = x_acc / xdata.count;
plot.ymin = 0.0;
plot.ymax = 0.0;
y_acc : float;
for ydata: yarrays {
yfloat := NewArray(ydata.count, float);
array_add(*plot.yfloats, yfloat);
for ydata {
yfloat[it_index] = cast(float) it;
if it > plot.ymax plot.ymax = xx it;
if it < plot.ymin plot.ymin = xx it;
is_nan, is_inf := is_nan_is_inf(cast(float) it);
if !is_nan && !is_inf {
y_acc += cast(float) it;
}
}
}
plot.y_avg = y_acc / (yarrays[0].count * yarrays.count);
if fixed_bounds {
plot.xmin = top_left.x;
plot.xmax = bottom_right.x;
plot.ymin = bottom_right.y;
plot.ymax = top_left.y;
plot.x_avg = plot.xmax - plot.xmin;
plot.y_avg = plot.ymax - plot.ymin;
}
}
plot.zoom = 1.0;
}
if data_style == .EVERY_FRAME {
plot.xfloat = NewArray(xdata.count, float);
plot.xmin = FLOAT32_MAX;
plot.xmax = -FLOAT32_MAX;
x_acc : float;
for xdata {
plot.xfloat[it_index] = cast(float) it;
if it > plot.xmax plot.xmax = xx it;
if it < plot.xmin plot.xmin = xx it;
is_nan, is_inf := is_nan_is_inf(cast(float) it);
if !is_nan && !is_inf {
x_acc += cast(float) it;
}
}
plot.x_avg = x_acc / xdata.count;
plot.ymin = 0.0;
plot.ymax = 0.0;
y_acc : float;
for ydata: yarrays {
yfloat := NewArray(ydata.count, float);
array_add(*plot.yfloats, yfloat);
for ydata {
yfloat[it_index] = cast(float) it;
if it > plot.ymax plot.ymax = xx it;
if it < plot.ymin plot.ymin = xx it;
is_nan, is_inf := is_nan_is_inf(cast(float) it);
if !is_nan && !is_inf {
y_acc += cast(float) it;
}
}
}
plot.y_avg = y_acc / (yarrays[0].count * yarrays.count);
if fixed_bounds {
plot.xmin = top_left.x;
plot.xmax = bottom_right.x;
plot.ymin = bottom_right.y;
plot.ymax = top_left.y;
plot.x_avg = plot.xmax - plot.xmin;
plot.y_avg = plot.ymax - plot.ymin;
}
}
ImGui.RadioButton("Tick marks", xx *plot.plot_style, 0);
ImGui.SameLine();
ImGui.RadioButton("Scale marker", xx *plot.plot_style, 1);
child_size := ImGui.GetContentRegionAvail();
child_size.y = 260;
child_pos := ImGui.GetCursorScreenPos();
if ImGui.BeginChild(key.data, child_size, ImGui.ChildFlags.None, ImGui.WindowFlags.HorizontalScrollbar) {
if plot.plot_style == {
case .TICK_MARKS;
plot.left_bearing = 40;
plot.bottom_bearing = 20;
case .SCALE_MARKER;
plot.left_bearing = 0;
plot.bottom_bearing = 20;
}
child_size = ImGui.GetContentRegionAvail();
child_pos = ImGui.GetCursorScreenPos();
plot.x = xx child_pos.x;
plot.y = xx child_pos.y;
if plot.width < 0 || plot.height < 0 {
log("[Warning] The plot has dimensions lower than 0.");
} else {
if plot.width != cast(s32) size.x || plot.height != cast(s32) size.y {
plot.width = xx size.x;
plot.height = xx size.y;
Simp.texture_resize_render_target(plot.texture, xx size.x, xx size.y);
}
ar : float = cast(float) plot.width / cast(float) plot.height;
pixel_size_x : float = (plot.xmax - plot.xmin) / plot.width;
pixel_size_y : float = (plot.ymax - plot.ymin) / plot.height;
if is_in_rect(xy(xx plot.x, xx plot.y), xy(xx plot.width, xx plot.height), mouse) {
if mouse_left {
plot.pos.x += mouse_delta.x * pixel_size_x / plot.zoom;
plot.pos.y += -mouse_delta.y * pixel_size_y / plot.zoom;
}
if mouse_wheel_zoom && !fixed_bounds {
plot.zoom += plot.zoom * (cast(float) mouse_wheel_delta * 0.1);
if plot.zoom < 0.01 {
plot.zoom = 0.01;
}
}
}
reset_fbo : GLint;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, *reset_fbo);
defer glBindFramebuffer(GL_FRAMEBUFFER, xx reset_fbo);
reset_program : GLint;
glGetIntegerv(GL_CURRENT_PROGRAM, *reset_program);
defer glUseProgram(xx reset_program);
Simp.set_render_target(plot.texture);
Simp.clear_render_target(colors.background.x, colors.background.y, colors.background.z, 1.0);
draw_axis(plot, plot.plot_style, colors);
if plot.plot_style == .SCALE_MARKER
draw_scale(plot, colors);
glUseProgram(data_shader);
glViewport(xx plot.left_bearing, xx plot.bottom_bearing,
xx (plot.width - plot.left_bearing), xx (plot.height - plot.bottom_bearing));
glBindVertexArray(plot.vao);
glBindBuffer(GL_ARRAY_BUFFER, plot.xvbo);
glEnableVertexAttribArray(0);
glBufferData(GL_ARRAY_BUFFER, plot.xfloat.count * size_of(float), plot.xfloat.data, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, size_of(float), null);
for yfloat : plot.yfloats {
glBindBuffer(GL_ARRAY_BUFFER, plot.yvbo);
glEnableVertexAttribArray(1);
glBufferData(GL_ARRAY_BUFFER, yfloat.count * size_of(float), yfloat.data, GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, size_of(float), null);
model := identity_of(Matrix4);
model = translate(model, .{plot.pos.x, plot.pos.y, 0.0});
model = translate(model, .{plot.x_avg, plot.y_avg, 0.0});
model = translate(model, .{-plot.pos.x, -plot.pos.y, 0.0});
model = scale(model, .{plot.zoom, plot.zoom, 1.0});
model = translate(model, .{plot.pos.x, plot.pos.y, 0.0});
model = translate(model, .{-plot.x_avg, -plot.y_avg, 0.0});
proj := orthographic_projection_matrix((plot.xmin),
(plot.xmax),
(plot.ymin),
(plot.ymax), -1.0, 1.0);
glUniformMatrix4fv(glGetUniformLocation(data_shader, "model"), 1, GL_TRUE, xx *model);
glUniformMatrix4fv(glGetUniformLocation(data_shader, "proj"), 1, GL_TRUE, xx *proj);
color := data_colors[it_index % data_colors.count];
glUniform4f(glGetUniformLocation(data_shader, "data_color"), color.x, color.y, color.z, color.w);
glDrawArrays(GL_LINE_STRIP, 0, xx (plot.xfloat.count));
primitives_rendered_this_frame += plot.xfloat.count - 1;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ImGui.Image(cast(ImGui.ImTextureID) plot.texture.gl_handle, size, .{0.0, 1.0}, .{1.0, 0.0});
plot.last_frame = frame;
}
}
ImGui.EndChild();
if data_style == .EVERY_FRAME {
array_reset(*plot.xfloat);
for * plot.yfloats {
array_reset(it);
remove it;
}
}
}
free_old_plots :: () {
for plot: plots {
if plot.last_frame < frame {
free_plot(plot);
table_remove(*plots, plot.key);
}
}
}
init :: (_window: Window_Type, _win_width: s32, _win_height: s32, _mouse_wheel_zoom : bool) {
window = _window;
win_width = _win_width;
win_height = _win_height;
mouse_wheel_zoom = _mouse_wheel_zoom;
JetBrainsMonoRegular = Simp.get_font_at_size("fonts", "JetBrainsMono-Regular.ttf", 14);
assert(JetBrainsMonoRegular != null);
data_shader = make_shader(data_vertex_shader_text, data_fragment_shader_text);
line_shader = make_shader(line_vertex_shader_text, line_fragment_shader_text);
glEnable(GL_MULTISAMPLE);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable(GL_LINE_SMOOTH);
glLineWidth(1.5);
}
window_resize :: (_win_width: s32, _win_height: s32) {
win_width = _win_width;
win_height = _win_height;
}
new_frame :: () {
frame += 1;
primitives_rendered_this_frame = 0;
mouse_delta = .{};
mouse_wheel_delta = 0;
x, y := get_mouse_pointer_position(window, false);
mouse = .{cast(float, x), cast(float, y)};
}
handle_input :: (events: []Input.Event) {
// @Hack Input module for android treats touch inputs as mouse button left, but you need to set the "mouse"
// position to something valid before triggering the click so update it here.
// @TODO(Charles): This can now be updated as modules/Input has touch! :ImGuiAndroidInput
#if OS == .ANDROID {
WC :: #import "Window_Creation";
Math :: #import "Math";
touch_x, touch_y, success := WC.get_mouse_pointer_position(true);
touch_x_f := cast(float) touch_x;
touch_y_f := cast(float) touch_y;
if !success {
// ImGui wants -FLT_MAX, -FLT_MAX to indicate no mouse.
FLT_MAX :: Math.FLOAT32_MAX;
touch_x_f = -FLT_MAX;
touch_y_f = -FLT_MAX;
}
// @Hack when releasing screen, need to have the mouse position set to where we released.
touch_on_release := mouse;
mouse = .{touch_x_f, touch_y_f};
}
// NOTE(Charles): On windows mouse position is handled in ImGui_ImplWin32_NewFrame with GetCusorPos on tick. The
// Imgui examples also respond to WM_MOUSEMOVE, but it's not clear why?
// modules/Input "doesn't have mouse move events yet", it only gives us the delta.
for event: events {
// :Win32InputMissing AddFocusEvent, could be done by checking Input.input_application_has_focus for change.
if event.type == {
case .KEYBOARD;
if event.key_code == {
// :Win32InputMissing mouse extra buttons, SetCapturing?, Mouse source
// case .MOUSE_BUTTON_LEFT; io.AddMouseButtonEvent(io, xx ImGui.MouseButton.Left , xx event.key_pressed);
case .MOUSE_BUTTON_LEFT;
// @Hack
#if OS == .ANDROID {
if !event.key_pressed {
mouse = .{touch_on_release.x, touch_on_release.y};
}
}
if event.key_pressed
mouse_left = true;
else
mouse_left = false;
case .MOUSE_BUTTON_RIGHT;
//io.AddMouseButtonEvent(io, xx ImGui.MouseButton.Right , xx event.key_pressed);
case .MOUSE_BUTTON_MIDDLE;
//io.AddMouseButtonEvent(io, xx ImGui.MouseButton.Middle, xx event.key_pressed);
case;
//imgui_key := to_imgui_key(event.key_code);
//// :Win32InputMissing Input only gives us single ctrl, shift, alt, not left/right. ImGui example sends
//// one event for either, then an extra for left right.
//if imgui_key != .None {
// // :Win32InputMissing SetKeyEventNativeData gets called for some legacy reason?
// io.AddKeyEvent(io, imgui_key, xx event.key_pressed);
//}
}
case .MOUSE_WHEEL;
mouse_wheel_delta += event.wheel_delta / cast(float, event.typical_wheel_delta);
}
}
mouse_delta += .{cast(float, Input.mouse_delta_x), cast(float, Input.mouse_delta_y)};
}
get_primitives_rendered :: () -> int {
return primitives_rendered_this_frame;
}
JetBrainsMonoRegular : *Simp.Dynamic_Font;
#scope_file
#import "Basic";
#import "GL";
#import "Hash_Table";
#import "Math";
ImGui :: #import,dir "../imgui-lib";
Simp :: #import "Simp";
#import "stb_rect_pack";
#import "File";
Input :: #import "Input";
#import "Window_Creation";
window : Window_Type;
frame : s64;
mouse : Vector2;
mouse_delta : Vector2;
mouse_wheel_delta : float;
mouse_left : bool;
primitives_rendered_this_frame : int;
win_width : s32;
win_height : s32;
mouse_wheel_zoom : bool;
PlotColors :: struct {
background : Vector3;
lines : Vector3;
text : Vector3;
}
Plot :: struct {
key : string;
plot_style : PLOT_STYLE;
texture: *Simp.Texture;
vao : GLuint;
xvbo : GLuint;
yvbo : GLuint;
xfloat : []float;
yfloats : [..][]float;
x : s32;
y : s32;
width : s32;
height : s32;
xmin : float;
xmax : float;
ymax : float;
ymin : float;
x_avg : float;
y_avg : float;
left_bearing : float;
bottom_bearing : float;
pos : Vector2;
zoom : float;
last_frame : s64;
}
plots : Table(string, *Plot);
data_colors := Vector4.[
.{1.0, 0.0, 0.0, 1.0},
.{0.0, 1.0, 0.0, 1.0},
.{0.0, 0.0, 1.0, 1.0},
.{0.76, 0.56, 0.1, 1.0},
.{0.8, 0.2, 0.2, 1.0},
.{0.1, 0.56, 0.48, 1.0},
];
free_plot :: (plot: *Plot) {
glDeleteVertexArrays(1, *plot.vao);
glDeleteBuffers(1, *plot.xvbo);
glDeleteBuffers(1, *plot.yvbo);
array_free(plot.xfloat);
for plot.yfloats {
array_free(it);
}
}
init_plot :: (plot: *Plot, key: string, width: s32, height: s32) {
plot.key = key;
plot.width = 1024;
plot.height = 1024;
plot.texture = Simp.texture_create_render_target(width, height, .RGBA8, .sRGB);
glGenVertexArrays(1, *plot.vao);
glBindVertexArray(plot.vao);
glGenBuffers(1, *plot.xvbo);
glGenBuffers(1, *plot.yvbo);
}
truncate :: (x: float, n: s32) -> float {
ret : float = x;
ret *= pow(10.0, xx n);
ret = floor(ret);
ret /= pow(10.0, xx n);
return ret;
}
draw_axis :: (using plot: Plot, style: PLOT_STYLE, colors: PlotColors) {
ar : float = cast(float) width / cast(float) height;
content_x : s32 = x + xx left_bearing;
content_y : s32;
content_width : s32 = width - xx left_bearing;
content_height : s32 = height - xx bottom_bearing;
extent_x := xmax - xmin;
extent_y := ymax - ymin;
positions : [..]Vector2;
defer array_reset(*positions);
array_add(*positions, .{0.0, 0.0});
array_add(*positions, .{xx (content_width ), 0.0});
array_add(*positions, .{xx (content_width ), 0.0});
array_add(*positions, .{xx (content_width ), xx (content_height )});
array_add(*positions, .{xx (content_width ), xx (content_height )});
array_add(*positions, .{0.0, xx (content_height )});
array_add(*positions, .{0.0, xx (content_height )});
array_add(*positions, .{0.0, 0.0});
if plot.xfloat.count > 0 {
x_ticks := 10;
for 0..x_ticks {
xpos : s64 = cast(s64) (cast(float) it / cast(float) x_ticks * cast(float) (content_width));
xxx := (((cast(float) it / cast(float) x_ticks) - 0.5) / zoom) + 0.5;
xxx = xxx * (xmax - (pos.x)) + (1 - xxx) * (xmin - (pos.x));
array_add(*positions, .{cast(float) xpos, 0.0});
array_add(*positions, .{cast(float) xpos, cast(float) (content_height )});
label_width := Simp.prepare_text(JetBrainsMonoRegular, tprint("%", formatFloat(cast(float) xxx,
trailing_width = 1, zero_removal = .NO)));
if style == .TICK_MARKS {
Simp.draw_prepared_text(JetBrainsMonoRegular,
xx (xpos + left_bearing - (label_width / 2.0)),
xx (4.0),
.{1, 1, 1, 1});
}
}
y_ticks := 10;
for 0..y_ticks {
ypos : s64 = cast(s64) (cast(float) it / cast(float) y_ticks * (content_height ));
yyy := ((0.5) - cast(float) it / cast(float) y_ticks) / zoom + 0.5;
yyy = yyy * (ymin - pos.y) + (1 - yyy) * (ymax - pos.y);
array_add(*positions, .{0.0, cast(float) ypos});
array_add(*positions, .{cast(float) (content_width ), cast(float) ypos});
label_width := Simp.prepare_text(JetBrainsMonoRegular, tprint("%", formatFloat(cast(float) yyy, trailing_width = 1,
zero_removal = .NO)));
if style == .TICK_MARKS {
Simp.draw_prepared_text(JetBrainsMonoRegular,
xx (xx cast(s64) (left_bearing - label_width - 4.0)),
xx (cast(s64) (ypos + JetBrainsMonoRegular.character_height)),
.{1, 1, 1, 1});
}
}
}
glViewport(xx left_bearing, xx bottom_bearing, xx (width - left_bearing), xx (height - bottom_bearing));
axis_vao : GLuint;
glGenVertexArrays(1, *axis_vao);
glBindVertexArray(axis_vao);
axis_vbo : GLuint;
glGenBuffers(1, *axis_vbo);
glBindBuffer(GL_ARRAY_BUFFER, axis_vbo);
glBufferData(GL_ARRAY_BUFFER, positions.count * size_of(Vector2), positions.data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, size_of(Vector2), null);
proj := orthographic_projection_matrix(0.0, cast(float) (content_width ), 0.0, cast(float) (content_height ), -1.0, 1.0);
glUseProgram(line_shader);
glUniformMatrix4fv(glGetUniformLocation(line_shader, "proj"), 1, GL_TRUE, xx *proj);
glUniform4f(glGetUniformLocation(line_shader, "data_color"), colors.lines.x, colors.lines.y, colors.lines.z, 1.0);
glDrawArrays(GL_LINES, 0, xx positions.count);
primitives_rendered_this_frame += positions.count / 2;
glDeleteBuffers(1, *axis_vbo);
glDeleteVertexArrays(1, *axis_vao);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
draw_scale :: (using plot: Plot, colors: PlotColors) {
ar : float = cast(float) width / cast(float) height;
content_x : s32 = x + xx left_bearing;
content_y : s32;
content_width : s32 = width - xx left_bearing;
content_height : s32 = height - xx bottom_bearing;
extent_x := xmax - xmin;
extent_y := ymax - ymin;
positions : [..]Vector2;
defer array_reset(*positions);
array_add(*positions, .{1.0, 1.0});
array_add(*positions, .{1.0 + content_width / 10.0, 1.0});
glViewport(0, 0, xx width, xx (height));
label_width := Simp.prepare_text(JetBrainsMonoRegular, tprint("%", formatFloat(cast(float) extent_x / zoom / 10.0,
trailing_width = 1, zero_removal = .NO)));
Simp.draw_prepared_text(JetBrainsMonoRegular,
xx (5.0 + content_width / 10.0),
xx (0),
.{1, 1, 1, 1});
array_add(*positions, .{1.0, 1.0});
array_add(*positions, .{1.0, 1.0 + content_height / 10.0});
label_width = Simp.prepare_text(JetBrainsMonoRegular, tprint("%", formatFloat(cast(float) extent_y / zoom / 10.0,
trailing_width = 1, zero_removal = .NO)));
Simp.draw_prepared_text(JetBrainsMonoRegular,
xx (0),
xx (5.0 + content_height / 10.0),
.{1, 1, 1, 1});
//glViewport(xx left_bearing, xx bottom_bearing, xx (width - left_bearing), xx (height - bottom_bearing));
scale_vao : GLuint;
glGenVertexArrays(1, *scale_vao);
glBindVertexArray(scale_vao);
scale_vbo : GLuint;
glGenBuffers(1, *scale_vbo);
glBindBuffer(GL_ARRAY_BUFFER, scale_vbo);
glBufferData(GL_ARRAY_BUFFER, positions.count * size_of(Vector2), positions.data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, size_of(Vector2), null);
proj := orthographic_projection_matrix(0.0, cast(float) (width), 0.0, cast(float) (height), -1.0, 1.0);
glUseProgram(line_shader);
glUniformMatrix4fv(glGetUniformLocation(line_shader, "proj"), 1, GL_TRUE, xx *proj);
glUniform4f(glGetUniformLocation(line_shader, "data_color"), 1.0, 0.0, 0.0, 1.0);
glDrawArrays(GL_LINES, 0, xx positions.count);
primitives_rendered_this_frame += positions.count / 2;
glDeleteBuffers(1, *scale_vbo);
glDeleteVertexArrays(1, *scale_vao);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
is_in_rect :: (pos: Vector2, size: Vector2, test: Vector2) -> bool {
top_left : Vector2 = pos;
bottom_right : Vector2 = pos + size;
if (test.x < top_left.x) return false;
if (test.y < top_left.y) return false;
if (test.x > bottom_right.x) return false;
if (test.y > bottom_right.y) return false;
return true;
}
data_shader : GLuint;
line_shader : GLuint;
make_shader :: (vertex_shader_text: string, fragment_shader_text: string) -> GLuint {
success : s32;
info : [512]u8;
vertex_shader := glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, *vertex_shader_text.data, null);
glCompileShader(vertex_shader);
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, *success);
if !success {
glGetShaderInfoLog(vertex_shader, 512, null, info.data);
log("[ERROR] SHADER VERTEX COMPILATION_FAILED %", to_string(info.data));
}
fragment_shader := glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, *fragment_shader_text.data, null);
glCompileShader(fragment_shader);
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, *success);
if !success {
glGetShaderInfoLog(fragment_shader, 512, null, info.data);
log("[ERROR] SHADER VERTEX COMPILATION_FAILED %", to_string(info.data));
}
shader := glCreateProgram();
glAttachShader(shader, vertex_shader);
glAttachShader(shader, fragment_shader);
glLinkProgram(shader);
glGetProgramiv(shader, GL_LINK_STATUS, *success);
if !success {
glGetProgramInfoLog(shader, 512, null, info.data);
log("[ERROR] SHADER PROGRAM LINKING_FAILED %", to_string(info.data));
}
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
return shader;
}
///////////////////////////////////////////////////
// shader to render the lines representing the data
///////////////////////////////////////////////////
data_vertex_shader_text : string = #string DONE
#version 330 core
layout (location = 0) in float data_x;
layout (location = 1) in float data_y;
uniform mat4 model;
uniform mat4 proj;
void main() {
gl_Position = proj * model * vec4(data_x, data_y, 0.0, 1.0);
}
DONE
data_fragment_shader_text : string = #string DONE
#version 330 core
uniform vec4 data_color;
out vec4 color;
float float_to_srgb(float l) {
if (l < 0.0031308) {
return l * 12.92;
} else {
return 1.055 * pow(l, 0.41666) - 0.055;
}
}
void main() {
color = vec4(data_color.rgb, float_to_srgb(data_color.a));
}
DONE
//////////////////////////////////////////////////////////
// general line shader for axis, squares, tick marks, etc.
//////////////////////////////////////////////////////////
line_vertex_shader_text : string = #string DONE
#version 330 core
layout (location = 0) in vec2 data_pos;
uniform mat4 proj;
void main() {
gl_Position = proj * vec4(data_pos.x, data_pos.y, 0.0, 1.0);
}
DONE
line_fragment_shader_text : string = #string DONE
#version 330 core
uniform vec4 data_color;
out vec4 color;
float float_to_srgb(float l) {
if (l < 0.0031308) {
return l * 12.92;
} else {
return 1.055 * pow(l, 0.41666) - 0.055;
}
}
void main() {
color = vec4(data_color.rgb, float_to_srgb(data_color.a));
}
DONE