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