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