35 #include <GL/glew.h> |
35 #include <GL/glew.h> |
36 |
36 |
37 typedef struct asc_rectangle_shader_s { |
37 typedef struct asc_rectangle_shader_s { |
38 AscShaderProgram program; |
38 AscShaderProgram program; |
39 GLint color; |
39 GLint color; |
|
40 GLint border_color; |
40 GLint size; |
41 GLint size; |
41 GLint thickness; |
42 GLint thickness; |
42 GLint radius; |
43 GLint radius; |
43 } AscRectangleShader; |
44 } AscRectangleShader; |
44 |
45 |
45 #define ASC_RECTANGLE_SHADER_FLAG_FILL 1 |
46 #define ASC_RECTANGLE_SHADER_FLAG_FILL 1 |
46 #define ASC_RECTANGLE_SHADER_FLAG_ROUND 2 |
47 #define ASC_RECTANGLE_SHADER_FLAG_ROUND 2 |
|
48 #define ASC_RECTANGLE_SHADER_FLAG_BORDER 4 |
47 |
49 |
48 static AscShaderProgram *asc_rectangle_shader_create(int flags) { |
50 static AscShaderProgram *asc_rectangle_shader_create(int flags) { |
49 AscShaderCodes codes; |
51 AscShaderCodes codes; |
|
52 // TODO: create a utility that rolls out those defines |
50 const char * const defines[] = { |
53 const char * const defines[] = { |
51 "", |
54 NULL, |
52 "#define FILL", |
55 "#define FILL", |
53 "#define ROUNDED_CORNERS", |
56 "#define BORDER\n#define ROUNDED_CORNERS", |
54 "#define FILL\n#define ROUNDED_CORNERS", |
57 "#define FILL\n#define ROUNDED_CORNERS", |
|
58 "#define BORDER", |
|
59 "#define BORDER\n#define FILL", |
|
60 "#define BORDER\n#define ROUNDED_CORNERS", |
|
61 "#define BORDER\n#define FILL\n#define ROUNDED_CORNERS", |
55 }; |
62 }; |
56 if (asc_shader_load_code_files((AscShaderCodeInfo){ |
63 if (asc_shader_load_code_files((AscShaderCodeInfo){ |
57 .files.vtx = "sprite_vtx.glsl", |
64 .files.vtx = "sprite_vtx.glsl", |
58 .files.frag = "rectangle_frag.glsl", |
65 .files.frag = "rectangle_frag.glsl", |
59 .defines.frag = defines[flags], |
66 .defines.frag = defines[flags], |
64 AscRectangleShader *shader = asc_shader_create(codes, sizeof(*shader)); |
71 AscRectangleShader *shader = asc_shader_create(codes, sizeof(*shader)); |
65 if (asc_has_error()) { |
72 if (asc_has_error()) { |
66 asc_shader_free_codes(codes); |
73 asc_shader_free_codes(codes); |
67 return NULL; |
74 return NULL; |
68 } |
75 } |
69 shader->color = glGetUniformLocation(shader->program.gl_id, "color"); |
|
70 shader->size = glGetUniformLocation(shader->program.gl_id, "size"); |
76 shader->size = glGetUniformLocation(shader->program.gl_id, "size"); |
71 if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_FILL)) { |
77 if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_FILL)) { |
|
78 shader->color = glGetUniformLocation(shader->program.gl_id, "color"); |
|
79 } else { |
|
80 shader->color = -1; |
|
81 } |
|
82 if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_BORDER)) { |
|
83 shader->thickness = glGetUniformLocation(shader->program.gl_id, "thickness"); |
|
84 shader->border_color = glGetUniformLocation(shader->program.gl_id, "border_color"); |
|
85 } else { |
72 shader->thickness = -1; |
86 shader->thickness = -1; |
73 } else { |
87 shader->border_color = -1; |
74 shader->thickness = glGetUniformLocation(shader->program.gl_id, "thickness"); |
|
75 } |
88 } |
76 if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_ROUND)) { |
89 if (asc_test_flag(flags, ASC_RECTANGLE_SHADER_FLAG_ROUND)) { |
77 shader->radius = glGetUniformLocation(shader->program.gl_id, "radius"); |
90 shader->radius = glGetUniformLocation(shader->program.gl_id, "radius"); |
78 } else { |
91 } else { |
79 shader->radius = -1; |
92 shader->radius = -1; |
96 asc_mesh_plane_2d(&rectangle->mesh, .size = size, .uv_scale = size); |
109 asc_mesh_plane_2d(&rectangle->mesh, .size = size, .uv_scale = size); |
97 } |
110 } |
98 |
111 |
99 static void asc_rectangle_draw(const AscCamera *camera, const AscSceneNode *node) { |
112 static void asc_rectangle_draw(const AscCamera *camera, const AscSceneNode *node) { |
100 asc_ptr_cast(AscRectangle, rectangle, node); |
113 asc_ptr_cast(AscRectangle, rectangle, node); |
101 const bool filled = asc_test_flag(rectangle->flags, ASC_RECTANGLE_FILLED); |
114 const bool filled = rectangle->filled; |
102 const bool round = rectangle->radius > 0; |
115 const bool round = rectangle->radius > 0; |
|
116 const bool border = rectangle->thickness > 0; |
103 |
117 |
104 // Compute shader flags |
118 // Compute shader flags |
105 int shader_flags = 0; |
119 int shader_flags = 0; |
106 if (filled) shader_flags |= ASC_RECTANGLE_SHADER_FLAG_FILL; |
120 if (filled) shader_flags |= ASC_RECTANGLE_SHADER_FLAG_FILL; |
|
121 if (border) shader_flags |= ASC_RECTANGLE_SHADER_FLAG_BORDER; |
107 if (round) shader_flags |= ASC_RECTANGLE_SHADER_FLAG_ROUND; |
122 if (round) shader_flags |= ASC_RECTANGLE_SHADER_FLAG_ROUND; |
108 |
123 |
109 // Compute shader ID |
124 // Compute shader ID |
110 const int shader_ids[] = { |
125 const int shader_ids[] = { |
111 ASC_SHADER_RECTANGLE_DRAW, |
126 -1, // unused |
112 ASC_SHADER_RECTANGLE_FILL, |
127 ASC_SHADER_RECTANGLE_FILL, |
113 ASC_SHADER_RECTANGLE_DRAW_ROUND, |
128 ASC_SHADER_RECTANGLE_DRAW_ROUND, |
114 ASC_SHADER_RECTANGLE_FILL_ROUND, |
129 ASC_SHADER_RECTANGLE_FILL_ROUND, |
|
130 ASC_SHADER_RECTANGLE_DRAW, |
|
131 ASC_SHADER_RECTANGLE_FILL_BORDER, |
|
132 ASC_SHADER_RECTANGLE_DRAW_ROUND, |
|
133 ASC_SHADER_RECTANGLE_FILL_BORDER_ROUND, |
115 }; |
134 }; |
116 |
135 |
117 // Look up and activate shader |
136 // Look up and activate shader |
118 const AscRectangleShader *shader = asc_shader_lookup_or_create( |
137 const AscRectangleShader *shader = asc_shader_lookup_or_create( |
119 shader_ids[shader_flags], asc_rectangle_shader_create, shader_flags); |
138 shader_ids[shader_flags], asc_rectangle_shader_create, shader_flags); |
122 // Upload uniforms |
141 // Upload uniforms |
123 // TODO: uploading model matrix could be a helper function |
142 // TODO: uploading model matrix could be a helper function |
124 glUniformMatrix4fv(shader->program.model, 1, |
143 glUniformMatrix4fv(shader->program.model, 1, |
125 GL_FALSE, node->world_transform); |
144 GL_FALSE, node->world_transform); |
126 |
145 |
127 glUniform4f(shader->color, |
146 if (filled) { |
128 rectangle->color.red, |
147 glUniform4f(shader->color, |
129 rectangle->color.green, |
148 rectangle->color.red, |
130 rectangle->color.blue, |
149 rectangle->color.green, |
131 rectangle->color.alpha |
150 rectangle->color.blue, |
132 ); |
151 rectangle->color.alpha |
|
152 ); |
|
153 } |
133 glUniform2f(shader->size, rectangle->width, rectangle->height); |
154 glUniform2f(shader->size, rectangle->width, rectangle->height); |
134 |
155 |
135 if (!filled) { |
156 if (border) { |
136 glUniform1f(shader->thickness, rectangle->thickness); |
157 glUniform1f(shader->thickness, rectangle->thickness); |
|
158 glUniform4f(shader->border_color, |
|
159 rectangle->border_color.red, |
|
160 rectangle->border_color.green, |
|
161 rectangle->border_color.blue, |
|
162 rectangle->border_color.alpha |
|
163 ); |
137 } |
164 } |
138 if (round) { |
165 if (round) { |
139 glUniform1f(shader->radius, rectangle->radius); |
166 glUniform1f(shader->radius, rectangle->radius); |
140 } |
167 } |
141 |
168 |
156 rectangle->node.position.y = (float) args.y; |
183 rectangle->node.position.y = (float) args.y; |
157 rectangle->width = (float) args.width; |
184 rectangle->width = (float) args.width; |
158 rectangle->height = (float) args.height; |
185 rectangle->height = (float) args.height; |
159 } |
186 } |
160 |
187 |
161 rectangle->thickness = ASC_NONZERO_OR(1.f, args.thickness); |
|
162 rectangle->radius = (float)args.radius; |
188 rectangle->radius = (float)args.radius; |
163 rectangle->color = asc_col_itof(asc_context.ink); |
189 rectangle->color = asc_col_itof(asc_context.ink); |
164 |
190 rectangle->border_color = asc_col_itof(args.border_color); |
165 if (args.filled) { |
191 rectangle->filled = args.filled; |
166 asc_set_flag(rectangle->flags, ASC_RECTANGLE_FILLED); |
192 if (!args.filled && args.thickness == 0) { |
|
193 // when we do not fill the rectangle, we need a border |
|
194 rectangle->thickness = 1; |
|
195 } else { |
|
196 rectangle->thickness = args.thickness; |
|
197 } |
|
198 if (!args.filled && asc_col4_test_zero(args.border_color)) { |
|
199 // convenience fallback: |
|
200 // when we are drawing an outline but have no explicit border color, |
|
201 // use the active ink |
|
202 rectangle->border_color = rectangle->color; |
167 } |
203 } |
168 |
204 |
169 AscSceneNode *node = &rectangle->node; |
205 AscSceneNode *node = &rectangle->node; |
170 node->position.z = ASC_SCENE_2D_DEPTH_OFFSET; |
206 node->position.z = ASC_SCENE_2D_DEPTH_OFFSET; |
171 node->scale = asc_vec3f_one; |
207 node->scale = asc_vec3f_one; |