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