Switched to fixed point in gles triangle rasterization, and fixed wrong offset curve check collapsing the internal control points

This commit is contained in:
martinfouilleul 2023-02-02 11:40:22 +01:00
parent e0300e9e3c
commit 0796b2cbcd
6 changed files with 265 additions and 266 deletions

View File

@ -41,7 +41,7 @@ int main()
mp_window_bring_to_front(window); mp_window_bring_to_front(window);
mp_window_focus(window); mp_window_focus(window);
f32 dx = 17.000029, dy = 0; f32 dx = 0, dy = 0;
while(!mp_should_quit()) while(!mp_should_quit())
{ {
@ -67,30 +67,27 @@ int main()
case MP_EVENT_KEYBOARD_KEY: case MP_EVENT_KEYBOARD_KEY:
{ {
printf("key %i: %s\n",
event.key.code,
event.key.action == MP_KEY_PRESS ? "press" : (event.key.action == MP_KEY_RELEASE ? "release" : "repeat"));
if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT) if(event.key.action == MP_KEY_PRESS || event.key.action == MP_KEY_REPEAT)
{ {
if(event.key.code == MP_KEY_LEFT) if(event.key.code == MP_KEY_LEFT)
{ {
printf("left\n"); printf("left\n");
dx-=0.1; dx-=1.1;
} }
else if(event.key.code == MP_KEY_RIGHT) else if(event.key.code == MP_KEY_RIGHT)
{ {
printf("right\n"); printf("right\n");
dx+=0.1; dx+=1.1;
} }
else if(event.key.code == MP_KEY_UP) else if(event.key.code == MP_KEY_UP)
{ {
printf("up\n"); printf("up\n");
dy+=0.1; dy+=1.1;
} }
else if(event.key.code == MP_KEY_DOWN) else if(event.key.code == MP_KEY_DOWN)
{ {
printf("down\n"); printf("down\n");
dy-=0.1; dy-=1.1;
} }
} }
} break; } break;
@ -102,12 +99,10 @@ int main()
mg_surface_prepare(surface); mg_surface_prepare(surface);
printf("dx = %f, dy = %f\n", dx, dy);
// background // background
mg_set_color_rgba(1, 0, 1, 1); mg_set_color_rgba(1, 0, 1, 1);
mg_clear(); mg_clear();
/*
// head // head
mg_set_color_rgba(1, 1, 0, 1); mg_set_color_rgba(1, 1, 0, 1);
mg_circle_fill(dx+400, dy+300, 200); mg_circle_fill(dx+400, dy+300, 200);
@ -123,8 +118,6 @@ int main()
// eyes // eyes
mg_ellipse_fill(dx+330, dy+350, 30, 50); mg_ellipse_fill(dx+330, dy+350, 30, 50);
mg_ellipse_fill(dx+470, dy+350, 30, 50); mg_ellipse_fill(dx+470, dy+350, 30, 50);
*/
mg_rectangle_fill((int)(dx + 200), 200, (int)(dy+300), (int)(dy+300));
mg_flush(); mg_flush();
mg_surface_present(surface); mg_surface_present(surface);

9
rasterization_notes.txt Normal file
View File

@ -0,0 +1,9 @@
https://fgiesen.wordpress.com/2013/02/08/triangle-rasterization-in-practice/
https://github.com/rygorous/trirast/blob/master/main.cpp
https://joshbeam.com/articles/triangle_rasterization/
https://nlguillemot.wordpress.com/2016/07/10/rasterizer-notes/
https://web.archive.org/web/20120625103536/http://devmaster.net/forums/topic/1145-advanced-rasterization/

View File

@ -2,7 +2,7 @@
* *
* file: gles_canvas_shaders.h * file: gles_canvas_shaders.h
* note: string literals auto-generated by embed_text.py * note: string literals auto-generated by embed_text.py
* date: 01/022023 * date: 02/022023
* *
**********************************************************************/ **********************************************************************/
#ifndef __GLES_CANVAS_SHADERS_H__ #ifndef __GLES_CANVAS_SHADERS_H__
@ -36,32 +36,25 @@ const char* gles_canvas_fragment =
"layout(location = 0) uniform int indexCount;\n" "layout(location = 0) uniform int indexCount;\n"
"layout(location = 0) out vec4 fragColor;\n" "layout(location = 0) out vec4 fragColor;\n"
"\n" "\n"
"bool is_top_left(vec2 a, vec2 b)\n" "bool is_top_left(ivec2 a, ivec2 b)\n"
"{\n" "{\n"
" return( (a.y == b.y && b.x < a.x)\n" " return( (a.y == b.y && b.x < a.x)\n"
" ||(b.y < a.y));\n" " ||(b.y < a.y));\n"
"}\n" "}\n"
"\n" "\n"
"float orient2d(vec2 a, vec2 b, vec2 c)\n" "int orient2d(ivec2 a, ivec2 b, ivec2 p)\n"
"{\n" "{\n"
" //////////////////////////////////////////////////////////////////////////////////////////\n" " return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));\n"
" //TODO(martin): FIX this. This is a **horrible** quick hack to fix the precision issues\n"
" // arising when a, b, and c are close. But it degrades when a, c, and c\n"
" // are big. The proper solution is to change the expression to avoid\n"
" // precision loss but I'm too busy/lazy to do it now.\n"
" //////////////////////////////////////////////////////////////////////////////////////////\n"
" a *= 10.;\n"
" b *= 10.;\n"
" c *= 10.;\n"
" return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));\n"
"}\n" "}\n"
"\n" "\n"
"void main()\n" "void main()\n"
"{\n" "{\n"
" float subPixelFactor = 16.;\n"
"\n"
" vec4 pixelColor = vec4(0.0, 1.0, 0.0, 1.0);\n" " vec4 pixelColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
" vec4 currentColor = vec4(0., 0., 0., 1.0);\n" " vec4 currentColor = vec4(0., 0., 0., 1.0);\n"
"\n" "\n"
" vec2 samplePoint = gl_FragCoord.xy;\n" " ivec2 samplePoint = ivec2(gl_FragCoord.xy * subPixelFactor + vec2(0.5, 0.5));\n"
"\n" "\n"
" int currentZIndex = -1;\n" " int currentZIndex = -1;\n"
" int flipCount = 0;\n" " int flipCount = 0;\n"
@ -73,22 +66,22 @@ const char* gles_canvas_fragment =
" uint i1 = indexBuffer.elements[i+1];\n" " uint i1 = indexBuffer.elements[i+1];\n"
" uint i2 = indexBuffer.elements[i+2];\n" " uint i2 = indexBuffer.elements[i+2];\n"
"\n" "\n"
" vec2 p0 = vertexBuffer.elements[i0].pos;\n" " ivec2 p0 = ivec2(vertexBuffer.elements[i0].pos * subPixelFactor + vec2(0.5, 0.5));\n"
" vec2 p1 = vertexBuffer.elements[i1].pos;\n" " ivec2 p1 = ivec2(vertexBuffer.elements[i1].pos * subPixelFactor + vec2(0.5, 0.5));\n"
" vec2 p2 = vertexBuffer.elements[i2].pos;\n" " ivec2 p2 = ivec2(vertexBuffer.elements[i2].pos * subPixelFactor + vec2(0.5, 0.5));\n"
"\n" "\n"
" int zIndex = vertexBuffer.elements[i0].zIndex;\n" " int zIndex = vertexBuffer.elements[i0].zIndex;\n"
" vec4 color = vertexBuffer.elements[i0].color;\n" " vec4 color = vertexBuffer.elements[i0].color;\n"
"\n" "\n"
" //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge\n" " //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge\n"
" float cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;\n" " int cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;\n"
" if(cw < 0.)\n" " if(cw < 0)\n"
" {\n" " {\n"
" uint tmpIndex = i1;\n" " uint tmpIndex = i1;\n"
" i1 = i2;\n" " i1 = i2;\n"
" i2 = tmpIndex;\n" " i2 = tmpIndex;\n"
"\n" "\n"
" vec2 tmpPoint = p1;\n" " ivec2 tmpPoint = p1;\n"
" p1 = p2;\n" " p1 = p2;\n"
" p2 = tmpPoint;\n" " p2 = tmpPoint;\n"
" }\n" " }\n"
@ -101,14 +94,13 @@ const char* gles_canvas_fragment =
" int bias1 = is_top_left(p2, p0) ? 0 : -1;\n" " int bias1 = is_top_left(p2, p0) ? 0 : -1;\n"
" int bias2 = is_top_left(p0, p1) ? 0 : -1;\n" " int bias2 = is_top_left(p0, p1) ? 0 : -1;\n"
"\n" "\n"
" float w0 = orient2d(p1, p2, samplePoint);\n" " int w0 = orient2d(p1, p2, samplePoint);\n"
" float w1 = orient2d(p2, p0, samplePoint);\n" " int w1 = orient2d(p2, p0, samplePoint);\n"
" float w2 = orient2d(p0, p1, samplePoint);\n" " int w2 = orient2d(p0, p1, samplePoint);\n"
"\n" "\n"
" if((int(w0)+bias0) >= 0 && (int(w1)+bias1) >= 0 && (int(w2)+bias2) >= 0)\n" " if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)\n"
" {\n" " {\n"
" //TODO check cubic\n" " vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));\n"
" vec4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2);\n"
"\n" "\n"
" float eps = 0.0001;\n" " float eps = 0.0001;\n"
" if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)\n" " if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)\n"

View File

@ -23,32 +23,25 @@ layout(binding = 1) buffer indexBufferSSBO {
layout(location = 0) uniform int indexCount; layout(location = 0) uniform int indexCount;
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
bool is_top_left(vec2 a, vec2 b) bool is_top_left(ivec2 a, ivec2 b)
{ {
return( (a.y == b.y && b.x < a.x) return( (a.y == b.y && b.x < a.x)
||(b.y < a.y)); ||(b.y < a.y));
} }
float orient2d(vec2 a, vec2 b, vec2 c) int orient2d(ivec2 a, ivec2 b, ivec2 p)
{ {
////////////////////////////////////////////////////////////////////////////////////////// return((b.x-a.x)*(p.y-a.y) - (b.y-a.y)*(p.x-a.x));
//TODO(martin): FIX this. This is a **horrible** quick hack to fix the precision issues
// arising when a, b, and c are close. But it degrades when a, c, and c
// are big. The proper solution is to change the expression to avoid
// precision loss but I'm too busy/lazy to do it now.
//////////////////////////////////////////////////////////////////////////////////////////
a *= 10.;
b *= 10.;
c *= 10.;
return((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));
} }
void main() void main()
{ {
float subPixelFactor = 16.;
vec4 pixelColor = vec4(0.0, 1.0, 0.0, 1.0); vec4 pixelColor = vec4(0.0, 1.0, 0.0, 1.0);
vec4 currentColor = vec4(0., 0., 0., 1.0); vec4 currentColor = vec4(0., 0., 0., 1.0);
vec2 samplePoint = gl_FragCoord.xy; ivec2 samplePoint = ivec2(gl_FragCoord.xy * subPixelFactor + vec2(0.5, 0.5));
int currentZIndex = -1; int currentZIndex = -1;
int flipCount = 0; int flipCount = 0;
@ -60,22 +53,22 @@ void main()
uint i1 = indexBuffer.elements[i+1]; uint i1 = indexBuffer.elements[i+1];
uint i2 = indexBuffer.elements[i+2]; uint i2 = indexBuffer.elements[i+2];
vec2 p0 = vertexBuffer.elements[i0].pos; ivec2 p0 = ivec2(vertexBuffer.elements[i0].pos * subPixelFactor + vec2(0.5, 0.5));
vec2 p1 = vertexBuffer.elements[i1].pos; ivec2 p1 = ivec2(vertexBuffer.elements[i1].pos * subPixelFactor + vec2(0.5, 0.5));
vec2 p2 = vertexBuffer.elements[i2].pos; ivec2 p2 = ivec2(vertexBuffer.elements[i2].pos * subPixelFactor + vec2(0.5, 0.5));
int zIndex = vertexBuffer.elements[i0].zIndex; int zIndex = vertexBuffer.elements[i0].zIndex;
vec4 color = vertexBuffer.elements[i0].color; vec4 color = vertexBuffer.elements[i0].color;
//NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge //NOTE(martin): reorder triangle counter-clockwise and compute bias for each edge
float cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x; int cw = (p1 - p0).x*(p2 - p0).y - (p1 - p0).y*(p2 - p0).x;
if(cw < 0.) if(cw < 0)
{ {
uint tmpIndex = i1; uint tmpIndex = i1;
i1 = i2; i1 = i2;
i2 = tmpIndex; i2 = tmpIndex;
vec2 tmpPoint = p1; ivec2 tmpPoint = p1;
p1 = p2; p1 = p2;
p2 = tmpPoint; p2 = tmpPoint;
} }
@ -88,14 +81,13 @@ void main()
int bias1 = is_top_left(p2, p0) ? 0 : -1; int bias1 = is_top_left(p2, p0) ? 0 : -1;
int bias2 = is_top_left(p0, p1) ? 0 : -1; int bias2 = is_top_left(p0, p1) ? 0 : -1;
float w0 = orient2d(p1, p2, samplePoint); int w0 = orient2d(p1, p2, samplePoint);
float w1 = orient2d(p2, p0, samplePoint); int w1 = orient2d(p2, p0, samplePoint);
float w2 = orient2d(p0, p1, samplePoint); int w2 = orient2d(p0, p1, samplePoint);
if((int(w0)+bias0) >= 0 && (int(w1)+bias1) >= 0 && (int(w2)+bias2) >= 0) if((w0+bias0) >= 0 && (w1+bias1) >= 0 && (w2+bias2) >= 0)
{ {
//TODO check cubic vec4 cubic = (cubic0*float(w0) + cubic1*float(w1) + cubic2*float(w2))/(float(w0)+float(w1)+float(w2));
vec4 cubic = (cubic0*w0 + cubic1*w1 + cubic2*w2)/(w0+w1+w2);
float eps = 0.0001; float eps = 0.0001;
if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps) if(cubic.w*(cubic.x*cubic.x*cubic.x - cubic.y*cubic.z) <= eps)

View File

@ -1154,7 +1154,7 @@ void mg_offset_hull(int count, vec2* p, vec2* result, f32 offset)
{ {
////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////
//WARN: quick fix for coincident middle control points //WARN: quick fix for coincident middle control points
if(count == 4 && (p[1].x - p[2].x < 0.01) && (p[1].y - p[2].y < 0.01)) if(count == 4 && (fabs(p[1].x - p[2].x) < 0.01) && (fabs(p[1].y - p[2].y) < 0.01))
{ {
vec2 hull3[3] = {p[0], p[1], p[3]}; vec2 hull3[3] = {p[0], p[1], p[3]};
vec2 result3[3]; vec2 result3[3];
@ -1284,6 +1284,7 @@ void mg_render_stroke_quadratic(mg_canvas_data* canvas, vec2 p[4], u32 zIndex, m
// //
// we compute the maximum overshoot outside these bounds and split the curve at the corresponding parameter // we compute the maximum overshoot outside these bounds and split the curve at the corresponding parameter
//TODO: maybe refactor by using tolerance in the _check_, not in the computation of the overshoot
f32 tolerance = minimum(attributes->tolerance, 0.5 * attributes->width); f32 tolerance = minimum(attributes->tolerance, 0.5 * attributes->width);
f32 d2LowBound = Square(0.5 * attributes->width - attributes->tolerance); f32 d2LowBound = Square(0.5 * attributes->width - attributes->tolerance);
f32 d2HighBound = Square(0.5 * attributes->width + attributes->tolerance); f32 d2HighBound = Square(0.5 * attributes->width + attributes->tolerance);
@ -1316,7 +1317,6 @@ void mg_render_stroke_quadratic(mg_canvas_data* canvas, vec2 p[4], u32 zIndex, m
if(maxOvershoot > 0) if(maxOvershoot > 0)
{ {
//TODO(martin): split at maxErrorParameter and recurse
vec2 splitLeft[3]; vec2 splitLeft[3];
vec2 splitRight[3]; vec2 splitRight[3];
mg_quadratic_split(p, maxOvershootParameter, splitLeft, splitRight); mg_quadratic_split(p, maxOvershootParameter, splitLeft, splitRight);
@ -1442,6 +1442,7 @@ void mg_render_stroke_cubic(mg_canvas_data* canvas, vec2 p[4], u32 zIndex, mg_at
// //
// we compute the maximum overshoot outside these bounds and split the curve at the corresponding parameter // we compute the maximum overshoot outside these bounds and split the curve at the corresponding parameter
//TODO: maybe refactor by using tolerance in the _check_, not in the computation of the overshoot
f32 tolerance = minimum(attributes->tolerance, 0.5 * attributes->width); f32 tolerance = minimum(attributes->tolerance, 0.5 * attributes->width);
f32 d2LowBound = Square(0.5 * attributes->width - attributes->tolerance); f32 d2LowBound = Square(0.5 * attributes->width - attributes->tolerance);
f32 d2HighBound = Square(0.5 * attributes->width + attributes->tolerance); f32 d2HighBound = Square(0.5 * attributes->width + attributes->tolerance);
@ -1474,17 +1475,17 @@ void mg_render_stroke_cubic(mg_canvas_data* canvas, vec2 p[4], u32 zIndex, mg_at
if(maxOvershoot > 0) if(maxOvershoot > 0)
{ {
//TODO(martin): split at maxErrorParameter and recurse
vec2 splitLeft[4]; vec2 splitLeft[4];
vec2 splitRight[4]; vec2 splitRight[4];
mg_cubic_split(p, maxOvershootParameter, splitLeft, splitRight); mg_cubic_split(p, maxOvershootParameter, splitLeft, splitRight);
mg_render_stroke_cubic(canvas, splitLeft, zIndex, attributes); mg_render_stroke_cubic(canvas, splitLeft, zIndex, attributes);
mg_render_stroke_cubic(canvas, splitRight, zIndex, attributes); mg_render_stroke_cubic(canvas, splitRight, zIndex, attributes);
//TODO: render joint between the split curves
} }
else else
{ {
//NOTE(martin): push the actual fill commands for the offset contour //NOTE(martin): push the actual fill commands for the offset contour
u32 zIndex = mg_get_next_z_index(canvas); u32 zIndex = mg_get_next_z_index(canvas);
mg_render_fill_cubic(canvas, positiveOffsetHull, zIndex, attributes->color); mg_render_fill_cubic(canvas, positiveOffsetHull, zIndex, attributes->color);

408
todo.txt
View File

@ -1,198 +1,210 @@
[.] Check changes in macos version [.] Check changes in macos version
[x] Restructure macos version to use mp_app_internal.h/mp_app.c [x] Restructure macos version to use mp_app_internal.h/mp_app.c
[x] test new run loop structure on macos [x] test new run loop structure on macos
[x] Fix resize crash when there's no surface [x] Fix resize crash when there's no surface
[>] separate data for key and mouse event? [>] separate data for key and mouse event?
[>] Simplify event structs [>] Simplify event structs
[ ] use isARepeat in macos keyDown event and simplify update key state [ ] use isARepeat in macos keyDown event and simplify update key state
[x] use surfaces to define restricted drawing locations [x] use surfaces to define restricted drawing locations
[x] Implement with NSView subviews on osx [x] Implement with NSView subviews on osx
[/] Maybe switch to just using CALayers? [/] Maybe switch to just using CALayers?
[ ] Cleanup graphics backend compile-time/runtime selection [ ] Cleanup graphics backend compile-time/runtime selection
[ ] Cleanup graphics resource handles [ ] Cleanup graphics resource handles
[>>] OpenGL surface on OSX [.] GLES 3.0 surface on OSX
[>>] Port vector graphics to OpenGL on OSX [x] GLES 3.1 surface on Win32
[>] Check OpenGL vector graphics on win32 [.] GLES vector graphics on win32
[ ] Implement surfaces with child windows on win32 [x] Fix triangle rasterization precision issues
[/] Maybe implement compositing directly in d3d and opengl compat extension... -> we do not want to snap vertex coordinates to integers though, but use fixed point with 4 or 8 bits of subpixel precision
-> convert verts pos to fixed point
Windows port -> do orient2d in fixed point
------------ [!] Check precision/possible overflow when using barycentric coords
[.] Finish events handling
[x] window [>>] Investigate cubics flipping when curves are disabled
[x] mouse move/buttons/enter/leave [>>] Investigate bad curve splitting on the right?
[x] mouse wheel [ ] Multi-sampling
[.] keys
[!] set key label [>] Avoid first useless (degenerate) triangle on every path
[x] text input
[/] pathdrop [ ] Implement surfaces with child windows on win32
[/] Maybe implement compositing directly in d3d and opengl compat extension...
[x] Unify app struct and window structs for different platforms?
> define common app and window struct in mp_app_internal.h Windows port
> this file conditionally includes platform specific headers, win32_app.h, osx_app.h, etc... ------------
> these define a macro to fill the common app and window structures with platform specific stuff. [.] Finish events handling
> Common app/window proc are defined in mp_app.c [x] window
> Platform specific stuff is defined in platform specific files win32_app.c, osx_app.m, etc... [x] mouse move/buttons/enter/leave
(mp_app.c can 'see' platform specific stuff, so ObjectiveC defs pose a problem, but we can define id as void* [x] mouse wheel
when not in ObjC...) [.] keys
[!] set key label
[.] Implement input polling [x] text input
[ ] Simplify input polling API names [/] pathdrop
[/] Try to simplify input state and polling once we have UI usage code
[x] Unify app struct and window structs for different platforms?
[ ] Finish win32 window create flags and properties query/setting > define common app and window struct in mp_app_internal.h
> this file conditionally includes platform specific headers, win32_app.h, osx_app.h, etc...
[ ] Implement clipboard > these define a macro to fill the common app and window structures with platform specific stuff.
[ ] Implement file dialogs > Common app/window proc are defined in mp_app.c
[ ] Impement resource path... -> maybe in abstracted file handling > Platform specific stuff is defined in platform specific files win32_app.c, osx_app.m, etc...
(mp_app.c can 'see' platform specific stuff, so ObjectiveC defs pose a problem, but we can define id as void*
[ ] Clean backend selection (compile time and runtime) when not in ObjC...)
[ ] Finish OpenGL loader
[ ] Test compute shaders [.] Implement input polling
[ ] Initial version of vector graphics backend [ ] Simplify input polling API names
[ ] Check integration of UI. [/] Try to simplify input state and polling once we have UI usage code
[ ] Remove unused APIs [ ] Finish win32 window create flags and properties query/setting
[ ] Implement clipboard
[ ] Implement file dialogs
Misc [ ] Impement resource path... -> maybe in abstracted file handling
----
[ ] Clean-up file structure [ ] Clean backend selection (compile time and runtime)
[ ] Move stb libs to ext/ [ ] Finish OpenGL loader
[ ] Test compute shaders
[ ] Renaming/clean-up pass [ ] Initial version of vector graphics backend
[ ] Separate Internal/API functions in mp_app.c [ ] Check integration of UI.
[ ] Remove MP_EVENT_KEYBOARD_MODS
[ ] Rename MP_EVENT_HIDE/SHOW to MINMIZE/UNMINIMIZE [ ] Remove unused APIs
[ ] Remove frame
[ ] Remove sequence from char event?
[ ] Replace frame_event with mp_rect
[ ] Document/unify quit/request_quit etc Misc
[ ] Document/unify close/request_close etc ----
[ ] Cleanup window management [ ] Clean-up file structure
[ ] Remove unused run loop constructs [ ] Move stb libs to ext/
[ ]
[ ] Renaming/clean-up pass
Shortlist [ ] Separate Internal/API functions in mp_app.c
--------- [ ] Remove MP_EVENT_KEYBOARD_MODS
[x] let pass flat args in ui_size_push() and ui_box_set_size() [ ] Rename MP_EVENT_HIDE/SHOW to MINMIZE/UNMINIMIZE
[x] separate style stacks [ ] Remove frame
[x] animation time stack [ ] Remove sequence from char event?
[>] margins? as part of size, or different styling stack? [ ] Replace frame_event with mp_rect
[ ] Document/unify quit/request_quit etc
[ ] Let build code set target style directly, and animate from current to target [ ] Document/unify close/request_close etc
[ ] filter styles stack by tag [ ] Cleanup window management
[ ] image backgrounds/gradients? [ ] Remove unused run loop constructs
[ ] animating open/close widgets? [ ]
[.] Text box widget Shortlist
[ ] Draw selection ---------
[ ] Set cursor on click [x] let pass flat args in ui_size_push() and ui_box_set_size()
[ ] Scroll to cursor [x] separate style stacks
[x] animation time stack
Canvas Drawing [>] margins? as part of size, or different styling stack?
--------------
[.] Correctly handle resizing / viewport [ ] Let build code set target style directly, and animate from current to target
[x] associate surfaces with mp_windows [ ] filter styles stack by tag
[x] window resize resizes surface. Surface always renders to whole mp_window [ ] image backgrounds/gradients?
[x] Add ability to create sub-views, and create surfaces for these [ ] animating open/close widgets?
- window comes with a main view, so we can get a surface for the whole window
[>] Clean native mp_window_data struct (don't need to cache a lot of stuff here) [.] Text box widget
[ ] Draw selection
[.] Add images bliting [ ] Set cursor on click
[x] Clean, rename uv vs texUV stuff [ ] Scroll to cursor
[>] Destroy stuff
[>] More unified handle system Canvas Drawing
[.] Rounded images (sortof) --------------
[ ] path clipped images [.] Correctly handle resizing / viewport
[x] associate surfaces with mp_windows
[ ] Add color gradients? [x] window resize resizes surface. Surface always renders to whole mp_window
[ ] Make canvas implicit? [x] Add ability to create sub-views, and create surfaces for these
[/] Handle based error signaling - window comes with a main view, so we can get a surface for the whole window
[/] Allow polling events in main thread, and updating/rendering in background thread. [>] Clean native mp_window_data struct (don't need to cache a lot of stuff here)
[x] font metrics shouldn't depend on surface, & font [.] Add images bliting
shouldn't really depend on canvas either??? [x] Clean, rename uv vs texUV stuff
> meaning we should be able to pass fonts without canvas in ui [>] Destroy stuff
> and only pass canvas when drawing at the end... [>] More unified handle system
[.] Rounded images (sortof)
UI [ ] path clipped images
--
[x] Make gui context implicit? [ ] Add color gradients?
[x] Uniform ui_box struct + cache widgets [ ] Make canvas implicit?
[x] Prune unused boxes [/] Handle based error signaling
[.] Layout boxes [/] Allow polling events in main thread, and updating/rendering in background thread.
[x] basic two pass layout
[x] Layout from start or end [x] font metrics shouldn't depend on surface, & font
[>] Add overflow flags to layout, & solve conflicts shouldn't really depend on canvas either???
> meaning we should be able to pass fonts without canvas in ui
[x] Temporarily push transform and text flip when rendering UI, so that the coord system is y down, origin at top left > and only pass canvas when drawing at the end...
[x] Canvas render the same size on a high-dpi surface
> it works with abstract 'pixel' units, which are transformed to pixels in the shader, according to backing store scaling UI
--
[.] Style struct and style stack [x] Make gui context implicit?
[ ] Maybe use individual stack for different style attributes [x] Uniform ui_box struct + cache widgets
[x] Pass initial style in ui_begin_frame() [x] Prune unused boxes
[.] Layout boxes
[.] Draw boxes [x] basic two pass layout
[x] use flags to enable/disable drawing each feature [x] Layout from start or end
[ ] active/hovered transitions [>] Add overflow flags to layout, & solve conflicts
[x] Change input state handling a move it to app layer [x] Temporarily push transform and text flip when rendering UI, so that the coord system is y down, origin at top left
[x] Compute signals for ui_box [x] Canvas render the same size on a high-dpi surface
[x] Use ui_size_push() and pass axis instead of ui_width/height_push() > it works with abstract 'pixel' units, which are transformed to pixels in the shader, according to backing store scaling
[>] Use value is ui_size as margin when kind == text or == children?
[.] Style struct and style stack
[ ] Allow animating sizes according to hot/active? [ ] Maybe use individual stack for different style attributes
[x] Pass initial style in ui_begin_frame()
[ ] Basic helpers
[.] button [.] Draw boxes
[.] slider (or rather, scroll bar) [x] use flags to enable/disable drawing each feature
[.] simple spacers [ ] active/hovered transitions
[ ] have a flag for non-cached stuff
[.] scrolling panel [x] Change input state handling a move it to app layer
[ ] Allow/disallow scrolling in x/y [x] Compute signals for ui_box
[ ] Scroll with mousewheel [x] Use ui_size_push() and pass axis instead of ui_width/height_push()
[/] add margins to scrollbars (disallow scrollbars crossing) [>] Use value is ui_size as margin when kind == text or == children?
[?] Maybe let builder code handle "active"/"hot" state, since it [ ] Allow animating sizes according to hot/active?
depends on the widgets
[ ] On the other hand, this state must be set before layouting, in [ ] Basic helpers
particular font/fontSize -> maybe do a pass for static layout, [.] button
instead of doing it in box creation... [.] slider (or rather, scroll bar)
[ ] this way we can compute styling after user has set active/hot, but before layout [.] simple spacers
> Maybe just let user set style selector, and provide persistent state bits that can [ ] have a flag for non-cached stuff
> be used in any way? (to replace eg active/hot?) or perhaps not needed if we have just [.] scrolling panel
> 'dragging' state [ ] Allow/disallow scrolling in x/y
[ ] Scroll with mousewheel
[/] add margins to scrollbars (disallow scrollbars crossing)
[x] Mask mouse outside of parent rects -> maintain clip stack and clip mouse against it
[x] Mask mouse below panels/other widgets [?] Maybe let builder code handle "active"/"hot" state, since it
[x] popups and tooltips depends on the widgets
[x] allow pushing/popping boxes irrespective of parent/child relation [ ] On the other hand, this state must be set before layouting, in
[x] ui_begin_frame() prepares two containers, user ui goes in the first one particular font/fontSize -> maybe do a pass for static layout,
[x] tooltips and menus go to the second one instead of doing it in box creation...
[x] Add menus [ ] this way we can compute styling after user has set active/hot, but before layout
[ ] line editing widget > Maybe just let user set style selector, and provide persistent state bits that can
> be used in any way? (to replace eg active/hot?) or perhaps not needed if we have just
Misc > 'dragging' state
----
[x] Split metal surface and metal painter (but put them in the same compilation unit?)
[x] Have only one rect struct [x] Mask mouse outside of parent rects -> maintain clip stack and clip mouse against it
[x] Shorten mp_string to str8 [x] Mask mouse below panels/other widgets
[x] popups and tooltips
[ ] Better/Simpler time API [x] allow pushing/popping boxes irrespective of parent/child relation
[x] ui_begin_frame() prepares two containers, user ui goes in the first one
[/] Frame throttling [x] tooltips and menus go to the second one
[x] For now, we always wait on vblank during mg_surface_present(), regardless of target fps [x] Add menus
[ ] Then actually get the correct display interval from the surface's current monitor [ ] line editing widget
[ ] Allow waiting for more display interval than one? (ie allow throttling at 30fps for a 60fps display)
Misc
[/] split osx_app and move all platform independant stuff outside ----
[x] Split metal surface and metal painter (but put them in the same compilation unit?)
[ ] Sort out mg_matrix_push/pop() -> transform vs. set... [x] Have only one rect struct
[x] Shorten mp_string to str8
[ ] Better/Simpler time API
[/] Frame throttling
[x] For now, we always wait on vblank during mg_surface_present(), regardless of target fps
[ ] Then actually get the correct display interval from the surface's current monitor
[ ] Allow waiting for more display interval than one? (ie allow throttling at 30fps for a 60fps display)
[/] split osx_app and move all platform independant stuff outside
[ ] Sort out mg_matrix_push/pop() -> transform vs. set...