4cdc65f621cc6c6a7bae7896b768a098791d0547
[blender.git] / source / blender / src / glutil.c
1
2
3 /**
4  * $Id$
5  *
6  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version. The Blender
12  * Foundation also sells licenses for use in proprietary software under
13  * the Blender License.  See http://www.blender.org/BL/ for information
14  * about this.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  *
25  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
26  * All rights reserved.
27  *
28  * The Original Code is: all of this file.
29  *
30  * Contributor(s): none yet.
31  *
32  * ***** END GPL/BL DUAL LICENSE BLOCK *****
33  */
34
35 #include <math.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include "MEM_guardedalloc.h"
42
43 #include "DNA_vec_types.h"
44
45 #include "BKE_utildefines.h"
46
47 #include "BLI_arithb.h"
48 #include "BIF_gl.h"
49 #include "BIF_glutil.h"
50
51         /* Invert line handling */
52         
53 #define glToggle(mode, onoff)   (((onoff)?glEnable:glDisable)(mode))
54
55 void set_inverted_drawing(int enable) 
56 {
57         glLogicOp(enable?GL_INVERT:GL_COPY);
58
59         /* Use GL_BLEND_EQUATION_EXT on sgi (if we have it),
60          * apparently GL_COLOR_LOGIC_OP doesn't work on O2?
61          * Is this an sgi bug or our bug?
62          */
63 #if defined(__sgi) && defined(GL_BLEND_EQUATION_EXT)
64         glBlendEquationEXT(enable?GL_LOGIC_OP:GL_FUNC_ADD_EXT);
65         glToggle(GL_BLEND, enable);
66 #else
67         glToggle(GL_COLOR_LOGIC_OP, enable);
68 #endif
69
70         glToggle(GL_DITHER, !enable);
71 }
72
73 void sdrawXORline(int x0, int y0, int x1, int y1)
74 {
75         if(x0==x1 && y0==y1) return;
76
77         set_inverted_drawing(1);
78         
79         glBegin(GL_LINES);
80         glVertex2i(x0, y0);
81         glVertex2i(x1, y1);
82         glEnd();
83         
84         set_inverted_drawing(0);
85 }
86
87 void glutil_draw_front_xor_line(int x0, int y0, int x1, int y1)
88 {
89         glReadBuffer(GL_FRONT);
90         glDrawBuffer(GL_FRONT);
91         sdrawXORline(x0, y0, x1, y1);
92         glFlush();
93         glReadBuffer(GL_BACK);
94         glDrawBuffer(GL_BACK);
95         
96 }
97
98 void sdrawXORline4(int nr, int x0, int y0, int x1, int y1)
99 {
100         static short old[4][2][2];
101         static char flags[4]= {0, 0, 0, 0};
102         
103                 /* with builtin memory, max 4 lines */
104
105         set_inverted_drawing(1);
106                 
107         glBegin(GL_LINES);
108         if(nr== -1) { /* flush */
109                 for (nr=0; nr<4; nr++) {
110                         if (flags[nr]) {
111                                 glVertex2sv(old[nr][0]);
112                                 glVertex2sv(old[nr][1]);
113                                 flags[nr]= 0;
114                         }
115                 }
116         } else {
117                 if(nr>=0 && nr<4) {
118                         if(flags[nr]) {
119                                 glVertex2sv(old[nr][0]);
120                                 glVertex2sv(old[nr][1]);
121                         }
122
123                         old[nr][0][0]= x0;
124                         old[nr][0][1]= y0;
125                         old[nr][1][0]= x1;
126                         old[nr][1][1]= y1;
127                         
128                         flags[nr]= 1;
129                 }
130                 
131                 glVertex2i(x0, y0);
132                 glVertex2i(x1, y1);
133         }
134         glEnd();
135         
136         set_inverted_drawing(0);
137 }
138
139 void fdrawXORcirc(float xofs, float yofs, float rad)
140 {
141         set_inverted_drawing(1);
142
143         glPushMatrix();
144         glTranslatef(xofs, yofs, 0.0);
145         glutil_draw_lined_arc(0.0, M_PI*2.0, rad, 20);
146         glPopMatrix();
147
148         set_inverted_drawing(0);
149 }
150
151 void glutil_draw_filled_arc(float start, float angle, float radius, int nsegments) {
152         int i;
153         
154         glBegin(GL_TRIANGLE_FAN);
155         glVertex2f(0.0, 0.0);
156         for (i=0; i<nsegments; i++) {
157                 float t= (float) i/(nsegments-1);
158                 float cur= start + t*angle;
159                 
160                 glVertex2f(cos(cur)*radius, sin(cur)*radius);
161         }
162         glEnd();
163 }
164
165 void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments) {
166         int i;
167         
168         glBegin(GL_LINE_STRIP);
169         for (i=0; i<nsegments; i++) {
170                 float t= (float) i/(nsegments-1);
171                 float cur= start + t*angle;
172                 
173                 glVertex2f(cos(cur)*radius, sin(cur)*radius);
174         }
175         glEnd();
176 }
177
178 int glaGetOneInteger(int param)
179 {
180         GLint i;
181         glGetIntegerv(param, &i);
182         return i;
183 }
184
185 float glaGetOneFloat(int param)
186 {
187         GLfloat v;
188         glGetFloatv(param, &v);
189         return v;
190 }
191
192 void glaRasterPosSafe2f(float x, float y, float known_good_x, float known_good_y)
193 {
194         GLubyte dummy= 0;
195
196                 /* As long as known good coordinates are correct
197                  * this is guarenteed to generate an ok raster
198                  * position (ignoring potential (real) overflow
199                  * issues).
200                  */
201         glRasterPos2f(known_good_x, known_good_y);
202
203                 /* Now shift the raster position to where we wanted
204                  * it in the first place using the glBitmap trick.
205                  */
206         glBitmap(0, 0, 0, 0, x - known_good_x, y - known_good_y, &dummy);
207 }
208
209 static int get_cached_work_texture(int *w_r, int *h_r)
210 {
211         static GLint texid= -1;
212         static int tex_w= 256;
213         static int tex_h= 256;
214
215         if (texid==-1) {
216                 GLint ltexid= glaGetOneInteger(GL_TEXTURE_2D);
217                 unsigned char *tbuf;
218
219                 glGenTextures(1, &texid);
220
221                 glBindTexture(GL_TEXTURE_2D, texid);
222
223                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
224                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
225                 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
226
227                 tbuf= MEM_callocN(tex_w*tex_h*4, "tbuf");
228                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tbuf);
229                 MEM_freeN(tbuf);
230
231                 glBindTexture(GL_TEXTURE_2D, ltexid);
232         }
233
234         *w_r= tex_w;
235         *h_r= tex_h;
236         return texid;
237 }
238
239 void glaDrawPixelsTex(float x, float y, int img_w, int img_h, int format, void *rect)
240 {
241         unsigned char *uc_rect= (unsigned char*) rect;
242         float *f_rect= (float *)rect;
243         float xzoom= glaGetOneFloat(GL_ZOOM_X), yzoom= glaGetOneFloat(GL_ZOOM_Y);
244         int ltexid= glaGetOneInteger(GL_TEXTURE_2D);
245         int lrowlength= glaGetOneInteger(GL_UNPACK_ROW_LENGTH);
246         int subpart_x, subpart_y, tex_w, tex_h;
247         int texid= get_cached_work_texture(&tex_w, &tex_h);
248         int nsubparts_x= (img_w+(tex_w-1))/tex_w;
249         int nsubparts_y= (img_h+(tex_h-1))/tex_h;
250
251         glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
252         glBindTexture(GL_TEXTURE_2D, texid);
253
254         for (subpart_y=0; subpart_y<nsubparts_y; subpart_y++) {
255                 for (subpart_x=0; subpart_x<nsubparts_x; subpart_x++) {
256                         int subpart_w= (subpart_x==nsubparts_x-1)?(img_w-subpart_x*tex_w):tex_w;
257                         int subpart_h= (subpart_y==nsubparts_y-1)?(img_h-subpart_y*tex_h):tex_h;
258                         float rast_x= x+subpart_x*tex_w*xzoom;
259                         float rast_y= y+subpart_y*tex_h*yzoom;
260                         
261                         if(format==GL_FLOAT)
262                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, GL_RGBA, GL_FLOAT, &f_rect[(subpart_y*tex_w)*img_w*4 + (subpart_x*tex_w)*4]);
263                         else
264                                 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, GL_RGBA, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y*tex_w)*img_w*4 + (subpart_x*tex_w)*4]);
265                                 
266                         glColor3ub(255, 255, 255);
267                         glEnable(GL_TEXTURE_2D);
268                         glBegin(GL_QUADS);
269                         glTexCoord2f(0, 0);
270                         glVertex2f(rast_x, rast_y);
271
272                         glTexCoord2f((float) subpart_w/tex_w, 0);
273                         glVertex2f(rast_x+subpart_w*xzoom, rast_y);
274
275                         glTexCoord2f((float) subpart_w/tex_w, (float) subpart_h/tex_h);
276                         glVertex2f(rast_x+subpart_w*xzoom, rast_y+subpart_h*yzoom);
277
278                         glTexCoord2f(0, (float) subpart_h/tex_h);
279                         glVertex2f(rast_x, rast_y+subpart_h*yzoom);
280                         glEnd();
281                         glDisable(GL_TEXTURE_2D);
282                 }
283         }
284
285         glBindTexture(GL_TEXTURE_2D, ltexid);
286         glPixelStorei(GL_UNPACK_ROW_LENGTH, lrowlength);
287 }
288
289 void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, int row_w, int format, int type, void *rect)
290 {
291         float xzoom= glaGetOneFloat(GL_ZOOM_X);
292         float yzoom= glaGetOneFloat(GL_ZOOM_Y);
293                 
294                 /* The pixel space coordinate of the intersection of
295                  * the [zoomed] image with the origin.
296                  */
297         float ix= -x/xzoom;
298         float iy= -y/yzoom;
299         
300                 /* The maximum pixel amounts the image can be cropped
301                  * at the lower left without exceeding the origin.
302                  */
303         int off_x= floor(MAX2(ix, 0));
304         int off_y= floor(MAX2(iy, 0));
305                 
306                 /* The zoomed space coordinate of the raster position 
307                  * (starting at the lower left most unclipped pixel).
308                  */
309         float rast_x= x + off_x*xzoom;
310         float rast_y= y + off_y*yzoom;
311
312         GLfloat scissor[4];
313         int draw_w, draw_h;
314
315                 /* Determine the smallest number of pixels we need to draw
316                  * before the image would go off the upper right corner.
317                  * 
318                  * It may seem this is just an optimization but some graphics 
319                  * cards (ATI) freak out if there is a large zoom factor and
320                  * a large number of pixels off the screen (probably at some
321                  * level the number of image pixels to draw is getting multiplied
322                  * by the zoom and then clamped). Making sure we draw the
323                  * fewest pixels possible keeps everyone mostly happy (still
324                  * fails if we zoom in on one really huge pixel so that it
325                  * covers the entire screen).
326                  */
327         glGetFloatv(GL_SCISSOR_BOX, scissor);
328         draw_w = MIN2(img_w-off_x, ceil((scissor[2]-rast_x)/xzoom));
329         draw_h = MIN2(img_h-off_y, ceil((scissor[3]-rast_y)/yzoom));
330
331         if (draw_w>0 && draw_h>0) {
332                 int old_row_length = glaGetOneInteger(GL_UNPACK_ROW_LENGTH);
333
334                         /* Don't use safe RasterPos (slower) if we can avoid it. */
335                 if (rast_x>=0 && rast_y>=0) {
336                         glRasterPos2f(rast_x, rast_y);
337                 } else {
338                         glaRasterPosSafe2f(rast_x, rast_y, 0, 0);
339                 }
340
341                 glPixelStorei(GL_UNPACK_ROW_LENGTH, row_w);
342                 if(format==GL_LUMINANCE || format==GL_RED) {
343                         if(type==GL_FLOAT) {
344                                 float *f_rect= (float *)rect;
345                                 glDrawPixels(draw_w, draw_h, format, type, f_rect + (off_y*row_w + off_x));
346                         }
347                         else if(type==GL_INT || type==GL_UNSIGNED_INT) {
348                                 int *i_rect= (int *)rect;
349                                 glDrawPixels(draw_w, draw_h, format, type, i_rect + (off_y*row_w + off_x));
350                         }
351                 }
352                 else { /* RGBA */
353                         if(type==GL_FLOAT) {
354                                 float *f_rect= (float *)rect;
355                                 glDrawPixels(draw_w, draw_h, format, type, f_rect + (off_y*row_w + off_x)*4);
356                         }
357                         else if(type==GL_UNSIGNED_BYTE) {
358                                 unsigned char *uc_rect= (unsigned char *) rect;
359                                 glDrawPixels(draw_w, draw_h, format, type, uc_rect + (off_y*row_w + off_x)*4);
360                         }
361                 }
362                 
363                 glPixelStorei(GL_UNPACK_ROW_LENGTH,  old_row_length);
364         }
365 }
366
367 /* 2D Drawing Assistance */
368
369 void glaDefine2DArea(rcti *screen_rect)
370 {
371         int sc_w= screen_rect->xmax - screen_rect->xmin;
372         int sc_h= screen_rect->ymax - screen_rect->ymin;
373
374         glViewport(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
375         glScissor(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
376
377                 /* The 0.375 magic number is to shift the matrix so that
378                  * both raster and vertex integer coordinates fall at pixel
379                  * centers properly. For a longer discussion see the OpenGL
380                  * Programming Guide, Appendix H, Correctness Tips.
381                  */
382
383         glMatrixMode(GL_PROJECTION);
384         glLoadIdentity();
385         glOrtho(0.0, sc_w, 0.0, sc_h, -1, 1);
386         glTranslatef(0.375, 0.375, 0.0);
387
388         glMatrixMode(GL_MODELVIEW);
389         glLoadIdentity();
390 }
391
392 struct gla2DDrawInfo {
393         int orig_vp[4], orig_sc[4];
394         float orig_projmat[16], orig_viewmat[16];
395
396         rcti screen_rect;
397         rctf world_rect;
398
399         float wo_to_sc[2];
400 };
401
402 void gla2DGetMap(gla2DDrawInfo *di, rctf *rect) 
403 {
404         *rect= di->world_rect;
405 }
406
407 void gla2DSetMap(gla2DDrawInfo *di, rctf *rect) 
408 {
409         int sc_w, sc_h;
410         float wo_w, wo_h;
411
412         di->world_rect= *rect;
413         
414         sc_w= (di->screen_rect.xmax-di->screen_rect.xmin);
415         sc_h= (di->screen_rect.ymax-di->screen_rect.ymin);
416         wo_w= (di->world_rect.xmax-di->world_rect.xmin);
417         wo_h= (di->world_rect.ymax-di->world_rect.ymin);
418         
419         di->wo_to_sc[0]= sc_w/wo_w;
420         di->wo_to_sc[1]= sc_h/wo_h;
421 }
422
423
424 gla2DDrawInfo *glaBegin2DDraw(rcti *screen_rect, rctf *world_rect) 
425 {
426         gla2DDrawInfo *di= MEM_mallocN(sizeof(*di), "gla2DDrawInfo");
427         int sc_w, sc_h;
428         float wo_w, wo_h;
429
430         glGetIntegerv(GL_VIEWPORT, (GLint *)di->orig_vp);
431         glGetIntegerv(GL_SCISSOR_BOX, (GLint *)di->orig_sc);
432         glGetFloatv(GL_PROJECTION_MATRIX, (GLfloat *)di->orig_projmat);
433         glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)di->orig_viewmat);
434
435         di->screen_rect= *screen_rect;
436         if (world_rect) {
437                 di->world_rect= *world_rect;
438         } else {
439                 di->world_rect.xmin= di->screen_rect.xmin;
440                 di->world_rect.ymin= di->screen_rect.ymin;
441                 di->world_rect.xmax= di->screen_rect.xmax;
442                 di->world_rect.ymax= di->screen_rect.ymax;
443         }
444
445         sc_w= (di->screen_rect.xmax-di->screen_rect.xmin);
446         sc_h= (di->screen_rect.ymax-di->screen_rect.ymin);
447         wo_w= (di->world_rect.xmax-di->world_rect.xmin);
448         wo_h= (di->world_rect.ymax-di->world_rect.ymin);
449
450         di->wo_to_sc[0]= sc_w/wo_w;
451         di->wo_to_sc[1]= sc_h/wo_h;
452
453         glaDefine2DArea(&di->screen_rect);
454
455         return di;
456 }
457
458 void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *sc_x_r, int *sc_y_r)
459 {
460         *sc_x_r= (wo_x - di->world_rect.xmin)*di->wo_to_sc[0];
461         *sc_y_r= (wo_y - di->world_rect.ymin)*di->wo_to_sc[1];
462 }
463 void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int screen_r[2])
464 {
465         screen_r[0]= (world[0] - di->world_rect.xmin)*di->wo_to_sc[0];
466         screen_r[1]= (world[1] - di->world_rect.ymin)*di->wo_to_sc[1];
467 }
468
469 void glaEnd2DDraw(gla2DDrawInfo *di)
470 {
471         glViewport(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
472         glScissor(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
473         glMatrixMode(GL_PROJECTION);
474         glLoadMatrixf(di->orig_projmat);
475         glMatrixMode(GL_MODELVIEW);
476         glLoadMatrixf(di->orig_viewmat);
477
478         MEM_freeN(di);
479 }
480
481 /* **************** glPoint hack ************************ */
482
483 static int curmode=0;
484 static int pointhack=0;
485 static GLubyte Squaredot[16] = { 0xff,0xff,0xff,0xff,
486                                                                  0xff,0xff,0xff,0xff,
487                                                                  0xff,0xff,0xff,0xff, 
488                                                                  0xff,0xff,0xff,0xff};
489
490 void bglBegin(int mode)
491 {
492         curmode= mode;
493         
494         if(mode==GL_POINTS) {
495                 float value[4];
496                 glGetFloatv(GL_POINT_SIZE_RANGE, value);
497                 if(value[1]<2.0) {
498                         glGetFloatv(GL_POINT_SIZE, value);
499                         pointhack= floor(value[0]+0.5);
500                         if(pointhack>4) pointhack= 4;
501                 }
502                 else glBegin(mode);
503         }
504 }
505
506
507 void bglVertex3fv(float *vec)
508 {
509         switch(curmode) {
510         case GL_POINTS:
511                 if(pointhack) {
512                         glRasterPos3fv(vec);
513                         glBitmap(pointhack, pointhack, (float)pointhack/2.0, (float)pointhack/2.0, 0.0, 0.0, Squaredot);
514                 }
515                 else glVertex3fv(vec);
516                 break;
517         }
518 }
519
520 void bglVertex3f(float x, float y, float z)
521 {
522         switch(curmode) {
523         case GL_POINTS:
524                 if(pointhack) {
525                         glRasterPos3f(x, y, z);
526                         glBitmap(pointhack, pointhack, (float)pointhack/2.0, (float)pointhack/2.0, 0.0, 0.0, Squaredot);
527                 }
528                 else glVertex3f(x, y, z);
529                 break;
530         }
531 }
532
533 void bglVertex2fv(float *vec)
534 {
535         switch(curmode) {
536         case GL_POINTS:
537                 if(pointhack) {
538                         glRasterPos2fv(vec);
539                         glBitmap(pointhack, pointhack, (float)pointhack/2, pointhack/2, 0.0, 0.0, Squaredot);
540                 }
541                 else glVertex2fv(vec);
542                 break;
543         }
544 }
545
546
547 void bglEnd(void)
548 {
549         if(pointhack) pointhack= 0;
550         else glEnd();
551         
552 }
553
554 /* *************** glPolygonOffset hack ************* */
555
556 // both temporal, so here for now (ton)
557 #include "BKE_global.h"
558 #include "DNA_view3d_types.h"
559
560 /* dist is only for ortho now... */
561 void bglPolygonOffset(float dist) 
562 {
563         static float winmat[16], offset=0.0;    
564         
565         if(dist!=0.0) {
566                 float offs;
567                 
568                 // glEnable(GL_POLYGON_OFFSET_FILL);
569                 // glPolygonOffset(-1.0, -1.0);
570
571                 /* hack below is to mimic polygon offset */
572                 glMatrixMode(GL_PROJECTION);
573                 glGetFloatv(GL_PROJECTION_MATRIX, (float *)winmat);
574                 
575                 /* dist is from camera to center point */
576                 
577                 if(winmat[15]>0.5) offs= 0.00001*dist*G.vd->dist;  // ortho tweaking
578                 else offs= 0.0005*dist;  // should be clipping value or so...
579                 
580                 winmat[14]-= offs;
581                 offset+= offs;
582                 
583                 glLoadMatrixf(winmat);
584                 glMatrixMode(GL_MODELVIEW);
585         }
586         else {
587
588                 glMatrixMode(GL_PROJECTION);
589                 winmat[14]+= offset;
590                 offset= 0.0;
591                 glLoadMatrixf(winmat);
592                 glMatrixMode(GL_MODELVIEW);
593         }
594 }
595
596
597
598