Merge branch 'blender2.7'
[blender.git] / source / blender / gpu / intern / gpu_immediate_util.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup gpu
19  *
20  * GPU immediate mode drawing utilities
21  */
22
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "BLI_utildefines.h"
27 #include "BLI_math.h"
28
29 #include "GPU_immediate.h"
30 #include "GPU_immediate_util.h"
31 #include "GPU_matrix.h"
32
33 static const float cube_coords[8][3] = {
34         {-1, -1, -1},
35         {-1, -1, +1},
36         {-1, +1, -1},
37         {-1, +1, +1},
38         {+1, -1, -1},
39         {+1, -1, +1},
40         {+1, +1, -1},
41         {+1, +1, +1},
42 };
43 static const int cube_quad_index[6][4] = {
44         {0, 1, 3, 2},
45         {0, 2, 6, 4},
46         {0, 4, 5, 1},
47         {1, 5, 7, 3},
48         {2, 3, 7, 6},
49         {4, 6, 7, 5},
50 };
51 static const int cube_line_index[12][2] = {
52         {0, 1},
53         {0, 2},
54         {0, 4},
55         {1, 3},
56         {1, 5},
57         {2, 3},
58         {2, 6},
59         {3, 7},
60         {4, 5},
61         {4, 6},
62         {5, 7},
63         {6, 7},
64 };
65
66 void immRectf(uint pos, float x1, float y1, float x2, float y2)
67 {
68         immBegin(GPU_PRIM_TRI_FAN, 4);
69         immVertex2f(pos, x1, y1);
70         immVertex2f(pos, x2, y1);
71         immVertex2f(pos, x2, y2);
72         immVertex2f(pos, x1, y2);
73         immEnd();
74 }
75
76 void immRecti(uint pos, int x1, int y1, int x2, int y2)
77 {
78         immBegin(GPU_PRIM_TRI_FAN, 4);
79         immVertex2i(pos, x1, y1);
80         immVertex2i(pos, x2, y1);
81         immVertex2i(pos, x2, y2);
82         immVertex2i(pos, x1, y2);
83         immEnd();
84 }
85
86 void immRectf_fast(uint pos, float x1, float y1, float x2, float y2)
87 {
88         immVertex2f(pos, x1, y1);
89         immVertex2f(pos, x2, y1);
90         immVertex2f(pos, x2, y2);
91
92         immVertex2f(pos, x1, y1);
93         immVertex2f(pos, x2, y2);
94         immVertex2f(pos, x1, y2);
95 }
96
97 void immRectf_fast_with_color(uint pos, uint col, float x1, float y1, float x2, float y2, const float color[4])
98 {
99         immAttr4fv(col, color);
100         immVertex2f(pos, x1, y1);
101         immAttr4fv(col, color);
102         immVertex2f(pos, x2, y1);
103         immAttr4fv(col, color);
104         immVertex2f(pos, x2, y2);
105
106         immAttr4fv(col, color);
107         immVertex2f(pos, x1, y1);
108         immAttr4fv(col, color);
109         immVertex2f(pos, x2, y2);
110         immAttr4fv(col, color);
111         immVertex2f(pos, x1, y2);
112 }
113
114 void immRecti_fast_with_color(uint pos, uint col, int x1, int y1, int x2, int y2, const float color[4])
115 {
116         immAttr4fv(col, color);
117         immVertex2i(pos, x1, y1);
118         immAttr4fv(col, color);
119         immVertex2i(pos, x2, y1);
120         immAttr4fv(col, color);
121         immVertex2i(pos, x2, y2);
122
123         immAttr4fv(col, color);
124         immVertex2i(pos, x1, y1);
125         immAttr4fv(col, color);
126         immVertex2i(pos, x2, y2);
127         immAttr4fv(col, color);
128         immVertex2i(pos, x1, y2);
129 }
130
131 #if 0 /* more complete version in case we want that */
132 void immRecti_complete(int x1, int y1, int x2, int y2, const float color[4])
133 {
134         GPUVertFormat *format = immVertexFormat();
135         uint pos = add_attr(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
136         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
137         immUniformColor4fv(color);
138         immRecti(pos, x1, y1, x2, y2);
139         immUnbindProgram();
140 }
141 #endif
142
143 /**
144  * Pack color into 3 bytes
145  *
146  * This define converts a numerical value to the equivalent 24-bit
147  * color, while not being endian-sensitive. On little-endians, this
148  * is the same as doing a 'naive' indexing, on big-endian, it is not!
149  *
150  * \note BGR format (i.e. 0xBBGGRR)...
151  *
152  * \param x: color.
153  */
154 void imm_cpack(uint x)
155 {
156         immUniformColor3ub(((x) & 0xFF),
157                            (((x) >> 8) & 0xFF),
158                            (((x) >> 16) & 0xFF));
159 }
160
161 static void imm_draw_circle(
162         GPUPrimType prim_type, const uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments)
163 {
164         immBegin(prim_type, nsegments);
165         for (int i = 0; i < nsegments; ++i) {
166                 const float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
167                 immVertex2f(shdr_pos, x + (rad_x * cosf(angle)), y + (rad_y * sinf(angle)));
168         }
169         immEnd();
170 }
171
172 /**
173  * Draw a circle outline with the given \a radius.
174  * The circle is centered at \a x, \a y and drawn in the XY plane.
175  *
176  * \param shdr_pos: The vertex attribute number for position.
177  * \param x: Horizontal center.
178  * \param y: Vertical center.
179  * \param rad: The circle's radius.
180  * \param nsegments: The number of segments to use in drawing (more = smoother).
181  */
182 void imm_draw_circle_wire_2d(uint shdr_pos, float x, float y, float rad, int nsegments)
183 {
184         imm_draw_circle(GPU_PRIM_LINE_LOOP, shdr_pos, x, y, rad, rad, nsegments);
185 }
186
187 /**
188  * Draw a filled circle with the given \a radius.
189  * The circle is centered at \a x, \a y and drawn in the XY plane.
190  *
191  * \param shdr_pos: The vertex attribute number for position.
192  * \param x: Horizontal center.
193  * \param y: Vertical center.
194  * \param rad: The circle's radius.
195  * \param nsegments: The number of segments to use in drawing (more = smoother).
196  */
197 void imm_draw_circle_fill_2d(uint shdr_pos, float x, float y, float rad, int nsegments)
198 {
199         imm_draw_circle(GPU_PRIM_TRI_FAN, shdr_pos, x, y, rad, rad, nsegments);
200 }
201
202 void imm_draw_circle_wire_aspect_2d(uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments)
203 {
204         imm_draw_circle(GPU_PRIM_LINE_LOOP, shdr_pos, x, y, rad_x, rad_y, nsegments);
205 }
206 void imm_draw_circle_fill_aspect_2d(uint shdr_pos, float x, float y, float rad_x, float rad_y, int nsegments)
207 {
208         imm_draw_circle(GPU_PRIM_TRI_FAN, shdr_pos, x, y, rad_x, rad_y, nsegments);
209 }
210
211 static void imm_draw_circle_partial(
212         GPUPrimType prim_type, uint pos, float x, float y,
213         float rad, int nsegments, float start, float sweep)
214 {
215         /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
216         const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2);
217         const float angle_end   = -(DEG2RADF(sweep) - angle_start);
218         nsegments += 1;
219         immBegin(prim_type, nsegments);
220         for (int i = 0; i < nsegments; ++i) {
221                 const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
222                 const float angle_sin = sinf(angle);
223                 const float angle_cos = cosf(angle);
224                 immVertex2f(pos, x + rad * angle_cos, y + rad * angle_sin);
225         }
226         immEnd();
227 }
228
229 void imm_draw_circle_partial_wire_2d(
230         uint pos, float x, float y,
231         float rad, int nsegments, float start, float sweep)
232 {
233         imm_draw_circle_partial(GPU_PRIM_LINE_STRIP, pos, x, y, rad, nsegments, start, sweep);
234 }
235
236 static void imm_draw_disk_partial(
237         GPUPrimType prim_type, uint pos, float x, float y,
238         float rad_inner, float rad_outer, int nsegments, float start, float sweep)
239 {
240         /* to avoid artifacts */
241         const float max_angle = 3 * 360;
242         CLAMP(sweep, -max_angle, max_angle);
243
244         /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */
245         const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2);
246         const float angle_end   = -(DEG2RADF(sweep) - angle_start);
247         nsegments += 1;
248         immBegin(prim_type, nsegments * 2);
249         for (int i = 0; i < nsegments; ++i) {
250                 const float angle = interpf(angle_start, angle_end, ((float)i / (float)(nsegments - 1)));
251                 const float angle_sin = sinf(angle);
252                 const float angle_cos = cosf(angle);
253                 immVertex2f(pos, x + rad_inner * angle_cos, y + rad_inner * angle_sin);
254                 immVertex2f(pos, x + rad_outer * angle_cos, y + rad_outer * angle_sin);
255         }
256         immEnd();
257 }
258
259 /**
260  * Draw a filled arc with the given inner and outer radius.
261  * The circle is centered at \a x, \a y and drawn in the XY plane.
262  *
263  * \note Arguments are `gluPartialDisk` compatible.
264  *
265  * \param pos: The vertex attribute number for position.
266  * \param x: Horizontal center.
267  * \param y: Vertical center.
268  * \param rad_inner: The inner circle's radius.
269  * \param rad_outer: The outer circle's radius (can be zero).
270  * \param nsegments: The number of segments to use in drawing (more = smoother).
271  * \param start: Specifies the starting angle, in degrees, of the disk portion.
272  * \param sweep: Specifies the sweep angle, in degrees, of the disk portion.
273  */
274 void imm_draw_disk_partial_fill_2d(
275         uint pos, float x, float y,
276         float rad_inner, float rad_outer, int nsegments, float start, float sweep)
277 {
278         imm_draw_disk_partial(GPU_PRIM_TRI_STRIP, pos, x, y, rad_inner, rad_outer, nsegments, start, sweep);
279 }
280
281 static void imm_draw_circle_3D(
282         GPUPrimType prim_type, uint pos, float x, float y,
283         float rad, int nsegments)
284 {
285         immBegin(prim_type, nsegments);
286         for (int i = 0; i < nsegments; ++i) {
287                 float angle = (float)(2 * M_PI) * ((float)i / (float)nsegments);
288                 immVertex3f(pos, x + rad * cosf(angle), y + rad * sinf(angle), 0.0f);
289         }
290         immEnd();
291 }
292
293 void imm_draw_circle_wire_3d(uint pos, float x, float y, float rad, int nsegments)
294 {
295         imm_draw_circle_3D(GPU_PRIM_LINE_LOOP, pos, x, y, rad, nsegments);
296 }
297
298 void imm_draw_circle_fill_3d(uint pos, float x, float y, float rad, int nsegments)
299 {
300         imm_draw_circle_3D(GPU_PRIM_TRI_FAN, pos, x, y, rad, nsegments);
301 }
302
303 /**
304  * Draw a lined box.
305  *
306  * \param pos: The vertex attribute number for position.
307  * \param x1: left.
308  * \param y1: bottom.
309  * \param x2: right.
310  * \param y2: top.
311  */
312 void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
313 {
314         immBegin(GPU_PRIM_LINE_LOOP, 4);
315         immVertex2f(pos, x1, y1);
316         immVertex2f(pos, x1, y2);
317         immVertex2f(pos, x2, y2);
318         immVertex2f(pos, x2, y1);
319         immEnd();
320 }
321
322 void imm_draw_box_wire_3d(uint pos, float x1, float y1, float x2, float y2)
323 {
324         /* use this version when GPUVertFormat has a vec3 position */
325         immBegin(GPU_PRIM_LINE_LOOP, 4);
326         immVertex3f(pos, x1, y1, 0.0f);
327         immVertex3f(pos, x1, y2, 0.0f);
328         immVertex3f(pos, x2, y2, 0.0f);
329         immVertex3f(pos, x2, y1, 0.0f);
330         immEnd();
331 }
332
333 /**
334  * Draw a standard checkerboard to indicate transparent backgrounds.
335  */
336 void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2)
337 {
338         uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
339         immBindBuiltinProgram(GPU_SHADER_2D_CHECKER);
340
341         immUniform4f("color1", 0.15f, 0.15f, 0.15f, 1.0f);
342         immUniform4f("color2", 0.2f, 0.2f, 0.2f, 1.0f);
343         immUniform1i("size", 8);
344
345         immRectf(pos, x1, y1, x2, y2);
346
347         immUnbindProgram();
348 }
349
350 void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3])
351 {
352         float coords[ARRAY_SIZE(cube_coords)][3];
353
354         for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
355                 madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect);
356         }
357
358         immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2);
359         for (int i = 0; i < ARRAY_SIZE(cube_quad_index); i++) {
360                 immVertex3fv(pos, coords[cube_quad_index[i][0]]);
361                 immVertex3fv(pos, coords[cube_quad_index[i][1]]);
362                 immVertex3fv(pos, coords[cube_quad_index[i][2]]);
363
364                 immVertex3fv(pos, coords[cube_quad_index[i][0]]);
365                 immVertex3fv(pos, coords[cube_quad_index[i][2]]);
366                 immVertex3fv(pos, coords[cube_quad_index[i][3]]);
367         }
368         immEnd();
369 }
370
371 void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3])
372 {
373         float coords[ARRAY_SIZE(cube_coords)][3];
374
375         for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) {
376                 madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect);
377         }
378
379         immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2);
380         for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) {
381                 immVertex3fv(pos, coords[cube_line_index[i][0]]);
382                 immVertex3fv(pos, coords[cube_line_index[i][1]]);
383         }
384         immEnd();
385 }
386
387 /**
388  * Draw a cylinder. Replacement for gluCylinder.
389  * _warning_ : Slow, better use it only if you no other choices.
390  *
391  * \param pos: The vertex attribute number for position.
392  * \param nor: The vertex attribute number for normal.
393  * \param base: Specifies the radius of the cylinder at z = 0.
394  * \param top: Specifies the radius of the cylinder at z = height.
395  * \param height: Specifies the height of the cylinder.
396  * \param slices: Specifies the number of subdivisions around the z axis.
397  * \param stacks: Specifies the number of subdivisions along the z axis.
398  */
399 void imm_draw_cylinder_fill_normal_3d(
400         uint pos, uint nor, float base, float top, float height, int slices, int stacks)
401 {
402         immBegin(GPU_PRIM_TRIS, 6 * slices * stacks);
403         for (int i = 0; i < slices; ++i) {
404                 const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices);
405                 const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices);
406                 const float cos1 = cosf(angle1);
407                 const float sin1 = sinf(angle1);
408                 const float cos2 = cosf(angle2);
409                 const float sin2 = sinf(angle2);
410
411                 for (int j = 0; j < stacks; ++j) {
412                         float fac1 = (float)j / (float)stacks;
413                         float fac2 = (float)(j + 1) / (float)stacks;
414                         float r1 = base * (1.f - fac1) + top * fac1;
415                         float r2 = base * (1.f - fac2) + top * fac2;
416                         float h1 = height * ((float)j / (float)stacks);
417                         float h2 = height * ((float)(j + 1) / (float)stacks);
418
419                         float v1[3] = {r1 * cos2, r1 * sin2, h1};
420                         float v2[3] = {r2 * cos2, r2 * sin2, h2};
421                         float v3[3] = {r2 * cos1, r2 * sin1, h2};
422                         float v4[3] = {r1 * cos1, r1 * sin1, h1};
423                         float n1[3], n2[3];
424
425                         /* calc normals */
426                         sub_v3_v3v3(n1, v2, v1);
427                         normalize_v3(n1);
428                         n1[0] = cos1; n1[1] = sin1; n1[2] = 1 - n1[2];
429
430                         sub_v3_v3v3(n2, v3, v4);
431                         normalize_v3(n2);
432                         n2[0] = cos2; n2[1] = sin2; n2[2] = 1 - n2[2];
433
434                         /* first tri */
435                         immAttr3fv(nor, n2);
436                         immVertex3fv(pos, v1);
437                         immVertex3fv(pos, v2);
438                         immAttr3fv(nor, n1);
439                         immVertex3fv(pos, v3);
440
441                         /* second tri */
442                         immVertex3fv(pos, v3);
443                         immVertex3fv(pos, v4);
444                         immAttr3fv(nor, n2);
445                         immVertex3fv(pos, v1);
446                 }
447         }
448         immEnd();
449 }
450
451 void imm_draw_cylinder_wire_3d(uint pos, float base, float top, float height, int slices, int stacks)
452 {
453         immBegin(GPU_PRIM_LINES, 6 * slices * stacks);
454         for (int i = 0; i < slices; ++i) {
455                 const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices);
456                 const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices);
457                 const float cos1 = cosf(angle1);
458                 const float sin1 = sinf(angle1);
459                 const float cos2 = cosf(angle2);
460                 const float sin2 = sinf(angle2);
461
462                 for (int j = 0; j < stacks; ++j) {
463                         float fac1 = (float)j / (float)stacks;
464                         float fac2 = (float)(j + 1) / (float)stacks;
465                         float r1 = base * (1.f - fac1) + top * fac1;
466                         float r2 = base * (1.f - fac2) + top * fac2;
467                         float h1 = height * ((float)j / (float)stacks);
468                         float h2 = height * ((float)(j + 1) / (float)stacks);
469
470                         float v1[3] = {r1 * cos2, r1 * sin2, h1};
471                         float v2[3] = {r2 * cos2, r2 * sin2, h2};
472                         float v3[3] = {r2 * cos1, r2 * sin1, h2};
473                         float v4[3] = {r1 * cos1, r1 * sin1, h1};
474
475                         immVertex3fv(pos, v1);
476                         immVertex3fv(pos, v2);
477
478                         immVertex3fv(pos, v2);
479                         immVertex3fv(pos, v3);
480
481                         immVertex3fv(pos, v1);
482                         immVertex3fv(pos, v4);
483                 }
484         }
485         immEnd();
486 }
487
488 void imm_draw_cylinder_fill_3d(uint pos, float base, float top, float height, int slices, int stacks)
489 {
490         immBegin(GPU_PRIM_TRIS, 6 * slices * stacks);
491         for (int i = 0; i < slices; ++i) {
492                 const float angle1 = (float)(2 * M_PI) * ((float)i / (float)slices);
493                 const float angle2 = (float)(2 * M_PI) * ((float)(i + 1) / (float)slices);
494                 const float cos1 = cosf(angle1);
495                 const float sin1 = sinf(angle1);
496                 const float cos2 = cosf(angle2);
497                 const float sin2 = sinf(angle2);
498
499                 for (int j = 0; j < stacks; ++j) {
500                         float fac1 = (float)j / (float)stacks;
501                         float fac2 = (float)(j + 1) / (float)stacks;
502                         float r1 = base * (1.f - fac1) + top * fac1;
503                         float r2 = base * (1.f - fac2) + top * fac2;
504                         float h1 = height * ((float)j / (float)stacks);
505                         float h2 = height * ((float)(j + 1) / (float)stacks);
506
507                         float v1[3] = {r1 * cos2, r1 * sin2, h1};
508                         float v2[3] = {r2 * cos2, r2 * sin2, h2};
509                         float v3[3] = {r2 * cos1, r2 * sin1, h2};
510                         float v4[3] = {r1 * cos1, r1 * sin1, h1};
511
512                         /* first tri */
513                         immVertex3fv(pos, v1);
514                         immVertex3fv(pos, v2);
515                         immVertex3fv(pos, v3);
516
517                         /* second tri */
518                         immVertex3fv(pos, v3);
519                         immVertex3fv(pos, v4);
520                         immVertex3fv(pos, v1);
521                 }
522         }
523         immEnd();
524 }