0464942cdd677759e2aa5eb2dfa604a227a80dd9
[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 #ifdef WIN32
42 #include "BLI_winstuff.h"
43 #endif
44
45 #include "MEM_guardedalloc.h"
46
47 #include "DNA_vec_types.h"
48
49 #include "BIF_gl.h"
50 #include "BIF_glutil.h"
51
52         /* Invert line handling */
53         
54 #define glToggle(mode, onoff)   (((onoff)?glEnable:glDisable)(mode))
55
56 static void set_inverted_drawing(int enable) 
57 {
58         glLogicOp(enable?GL_INVERT:GL_COPY);
59
60         /* Use GL_BLEND_EQUATION_EXT on sgi (if we have it),
61          * apparently GL_COLOR_LOGIC_OP doesn't work on O2?
62          * Is this an sgi bug or our bug?
63          */
64 #if defined(__sgi) && defined(GL_BLEND_EQUATION_EXT)
65         glBlendEquationEXT(enable?GL_LOGIC_OP:GL_FUNC_ADD_EXT);
66         glToggle(GL_BLEND, enable);
67 #else
68         glToggle(GL_COLOR_LOGIC_OP, enable);
69 #endif
70
71         glToggle(GL_DITHER, !enable);
72 }
73
74 void sdrawXORline(int x0, int y0, int x1, int y1)
75 {
76         if(x0==x1 && y0==y1) return;
77
78         set_inverted_drawing(1);
79         
80         glBegin(GL_LINES);
81         glVertex2i(x0, y0);
82         glVertex2i(x1, y1);
83         glEnd();
84         
85         set_inverted_drawing(0);
86 }
87
88 void glutil_draw_front_xor_line(int x0, int y0, int x1, int y1)
89 {
90         glDrawBuffer(GL_FRONT);
91         sdrawXORline(x0, y0, x1, y1);
92         glFlush();
93         glDrawBuffer(GL_BACK);
94 }
95
96 void sdrawXORline4(int nr, int x0, int y0, int x1, int y1)
97 {
98         static short old[4][2][2];
99         static char flags[4]= {0, 0, 0, 0};
100         
101                 /* with builtin memory, max 4 lines */
102
103         set_inverted_drawing(1);
104                 
105         glBegin(GL_LINES);
106         if(nr== -1) { /* flush */
107                 for (nr=0; nr<4; nr++) {
108                         if (flags[nr]) {
109                                 glVertex2sv(old[nr][0]);
110                                 glVertex2sv(old[nr][1]);
111                                 flags[nr]= 0;
112                         }
113                 }
114         } else {
115                 if(nr>=0 && nr<4) {
116                         if(flags[nr]) {
117                                 glVertex2sv(old[nr][0]);
118                                 glVertex2sv(old[nr][1]);
119                         }
120
121                         old[nr][0][0]= x0;
122                         old[nr][0][1]= y0;
123                         old[nr][1][0]= x1;
124                         old[nr][1][1]= y1;
125                         
126                         flags[nr]= 1;
127                 }
128                 
129                 glVertex2i(x0, y0);
130                 glVertex2i(x1, y1);
131         }
132         glEnd();
133         
134         set_inverted_drawing(0);
135 }
136
137 void sdrawXORcirc(short xofs, short yofs, float rad)
138 {
139         set_inverted_drawing(1);
140
141         glPushMatrix();
142         glTranslatef(xofs, yofs, 0.0);
143         glutil_draw_lined_arc(0.0, M_PI*2, rad, 20);
144         glPopMatrix();
145
146         set_inverted_drawing(0);
147 }
148
149 void glutil_draw_filled_arc(float start, float angle, float radius, int nsegments) {
150         int i;
151         
152         glBegin(GL_TRIANGLE_FAN);
153         glVertex2f(0.0, 0.0);
154         for (i=0; i<nsegments; i++) {
155                 float t= (float) i/(nsegments-1);
156                 float cur= start + t*angle;
157                 
158                 glVertex2f(cos(cur)*radius, sin(cur)*radius);
159         }
160         glEnd();
161 }
162
163 void glutil_draw_lined_arc(float start, float angle, float radius, int nsegments) {
164         int i;
165         
166         glBegin(GL_LINE_STRIP);
167         for (i=0; i<nsegments; i++) {
168                 float t= (float) i/(nsegments-1);
169                 float cur= start + t*angle;
170                 
171                 glVertex2f(cos(cur)*radius, sin(cur)*radius);
172         }
173         glEnd();
174 }
175
176 int glaGetOneInteger(int param)
177 {
178         int i;
179         glGetIntegerv(param, &i);
180         return i;
181 }
182
183 float glaGetOneFloat(int param)
184 {
185         float v;
186         glGetFloatv(param, &v);
187         return v;
188 }
189
190 void glaRasterPosSafe2f(float x, float y, float known_good_x, float known_good_y)
191 {
192         GLubyte dummy= 0;
193
194                 /* As long as known good coordinates are correct
195                  * this is guarenteed to generate an ok raster
196                  * position (ignoring potential (real) overflow
197                  * issues).
198                  */
199         glRasterPos2f(known_good_x, known_good_y);
200
201                 /* Now shift the raster position to where we wanted
202                  * it in the first place using the glBitmap trick.
203                  */
204         glBitmap(0, 0, 0, 0, x - known_good_x, y - known_good_y, &dummy);
205 }
206
207 static int get_cached_work_texture(int *w_r, int *h_r)
208 {
209         static int texid= -1;
210         static int tex_w= 256;
211         static int tex_h= 256;
212
213         if (texid==-1) {
214                 GLint ltexid= glaGetOneInteger(GL_TEXTURE_2D);
215                 unsigned char *tbuf;
216
217                 glGenTextures(1, &texid);
218
219                 glBindTexture(GL_TEXTURE_2D, texid);
220
221                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
222                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
223                 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
224
225                 tbuf= MEM_callocN(tex_w*tex_h*4, "tbuf");
226                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tbuf);
227                 MEM_freeN(tbuf);
228
229                 glBindTexture(GL_TEXTURE_2D, ltexid);
230         }
231
232         *w_r= tex_w;
233         *h_r= tex_h;
234         return texid;
235 }
236
237 void glaDrawPixelsTex(float x, float y, int img_w, int img_h, void *rect)
238 {
239         unsigned char *uc_rect= (unsigned char*) rect;
240         float xzoom= glaGetOneFloat(GL_ZOOM_X), yzoom= glaGetOneFloat(GL_ZOOM_Y);
241         int ltexid= glaGetOneInteger(GL_TEXTURE_2D);
242         int lrowlength= glaGetOneInteger(GL_UNPACK_ROW_LENGTH);
243         int subpart_x, subpart_y, tex_w, tex_h;
244         int texid= get_cached_work_texture(&tex_w, &tex_h);
245         int nsubparts_x= (img_w+(tex_w-1))/tex_w;
246         int nsubparts_y= (img_h+(tex_h-1))/tex_h;
247
248         glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
249         glBindTexture(GL_TEXTURE_2D, texid);
250
251         for (subpart_y=0; subpart_y<nsubparts_y; subpart_y++) {
252                 for (subpart_x=0; subpart_x<nsubparts_x; subpart_x++) {
253                         int subpart_w= (subpart_x==nsubparts_x-1)?(img_w-subpart_x*tex_w):tex_w;
254                         int subpart_h= (subpart_y==nsubparts_y-1)?(img_h-subpart_y*tex_h):tex_h;
255                         float rast_x= x+subpart_x*tex_w*xzoom;
256                         float rast_y= y+subpart_y*tex_h*yzoom;
257
258                         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]);
259
260                         glColor3ub(255, 255, 255);
261                         glEnable(GL_TEXTURE_2D);
262                         glBegin(GL_QUADS);
263                         glTexCoord2f(0, 0);
264                         glVertex2f(rast_x, rast_y);
265
266                         glTexCoord2f((float) subpart_w/tex_w, 0);
267                         glVertex2f(rast_x+subpart_w*xzoom, rast_y);
268
269                         glTexCoord2f((float) subpart_w/tex_w, (float) subpart_h/tex_h);
270                         glVertex2f(rast_x+subpart_w*xzoom, rast_y+subpart_h*yzoom);
271
272                         glTexCoord2f(0, (float) subpart_h/tex_h);
273                         glVertex2f(rast_x, rast_y+subpart_h*yzoom);
274                         glEnd();
275                         glDisable(GL_TEXTURE_2D);
276                 }
277         }
278
279         glBindTexture(GL_TEXTURE_2D, ltexid);
280         glPixelStorei(GL_UNPACK_ROW_LENGTH, lrowlength);
281 }
282
283 void glaDrawPixelsSafe(float x, float y, int img_w, int img_h, void *rect)
284 {
285         unsigned char *uc_rect= (unsigned char*) rect;
286         float origin_x= 0.375;
287         float origin_y= 0.375;
288
289                 /* Trivial case */
290         if (x>=origin_x && y>=origin_y) {
291                 glRasterPos2f(x, y);
292                 glDrawPixels(img_w, img_h, GL_RGBA, GL_UNSIGNED_BYTE, uc_rect);
293         } else {
294                 int old_row_length= glaGetOneInteger(GL_UNPACK_ROW_LENGTH);
295                 float xzoom= glaGetOneFloat(GL_ZOOM_X);
296                 float yzoom= glaGetOneFloat(GL_ZOOM_Y);
297
298                         /* The pixel space coordinate of the intersection of
299                          * the [zoomed] image with the origin.
300                          */
301                 float ix= (origin_x-x)/xzoom;
302                 float iy= (origin_y-y)/yzoom;
303         
304                         /* The maximum pixel amounts the image can cropped
305                          * without exceeding the origin.
306                          */
307                 int off_x= floor((ix>origin_x)?ix:origin_x);
308                 int off_y= floor((iy>origin_y)?iy:origin_y);
309                 
310                         /* The zoomed space coordinate of the raster
311                          * position.
312                          */
313                 float rast_x= x + off_x*xzoom;
314                 float rast_y= y + off_y*yzoom;
315                 
316                 if (off_x<img_w && off_y<img_h) {
317                         glaRasterPosSafe2f(rast_x, rast_y, origin_x, origin_y);
318                         glPixelStorei(GL_UNPACK_ROW_LENGTH, img_w);
319                         glDrawPixels(img_w-off_x, img_h-off_y, GL_RGBA, GL_UNSIGNED_BYTE, uc_rect+off_y*img_w*4+off_x*4);
320                         glPixelStorei(GL_UNPACK_ROW_LENGTH,  old_row_length);
321                 }
322         }
323 }
324
325 /* 2D Drawing Assistance */
326
327 void glaDefine2DArea(rcti *screen_rect)
328 {
329         int sc_w= screen_rect->xmax - screen_rect->xmin;
330         int sc_h= screen_rect->ymax - screen_rect->ymin;
331
332         glViewport(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
333         glScissor(screen_rect->xmin, screen_rect->ymin, sc_w, sc_h);
334
335                 /* The 0.375 magic number is to shift the matrix so that
336                  * both raster and vertex integer coordinates fall at pixel
337                  * centers properly. For a longer discussion see the OpenGL
338                  * Programming Guide, Appendix H, Correctness Tips.
339                  */
340
341         glMatrixMode(GL_PROJECTION);
342         glLoadIdentity();
343         glOrtho(0.0, sc_w, 0.0, sc_h, -1, 1);
344         glTranslatef(0.375, 0.375, 0.0);
345
346         glMatrixMode(GL_MODELVIEW);
347         glLoadIdentity();
348 }
349
350 struct gla2DDrawInfo {
351         int orig_vp[4], orig_sc[4];
352         float orig_projmat[16], orig_viewmat[16];
353
354         rcti screen_rect;
355         rctf world_rect;
356
357         float wo_to_sc[2];
358 };
359
360 gla2DDrawInfo *glaBegin2DDraw(rcti *screen_rect, rctf *world_rect) 
361 {
362         gla2DDrawInfo *di= MEM_mallocN(sizeof(*di), "gla2DDrawInfo");
363         int sc_w, sc_h;
364         float wo_w, wo_h;
365
366         glGetIntegerv(GL_VIEWPORT, di->orig_vp);
367         glGetIntegerv(GL_SCISSOR_BOX, di->orig_sc);
368         glGetFloatv(GL_PROJECTION_MATRIX, di->orig_projmat);
369         glGetFloatv(GL_MODELVIEW_MATRIX, di->orig_viewmat);
370
371         di->screen_rect= *screen_rect;
372         if (world_rect) {
373                 di->world_rect= *world_rect;
374         } else {
375                 di->world_rect.xmin= di->screen_rect.xmin;
376                 di->world_rect.ymin= di->screen_rect.ymin;
377                 di->world_rect.xmax= di->screen_rect.xmax;
378                 di->world_rect.ymax= di->screen_rect.ymax;
379         }
380
381         sc_w= (di->screen_rect.xmax-di->screen_rect.xmin);
382         sc_h= (di->screen_rect.ymax-di->screen_rect.ymin);
383         wo_w= (di->world_rect.xmax-di->world_rect.xmin);
384         wo_h= (di->world_rect.ymax-di->world_rect.ymin);
385
386         di->wo_to_sc[0]= sc_w/wo_w;
387         di->wo_to_sc[1]= sc_h/wo_h;
388
389         glaDefine2DArea(&di->screen_rect);
390
391         return di;
392 }
393
394 void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *sc_x_r, int *sc_y_r)
395 {
396         *sc_x_r= (wo_x - di->world_rect.xmin)*di->wo_to_sc[0];
397         *sc_y_r= (wo_y - di->world_rect.ymin)*di->wo_to_sc[1];
398 }
399 void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int screen_r[2])
400 {
401         screen_r[0]= (world[0] - di->world_rect.xmin)*di->wo_to_sc[0];
402         screen_r[1]= (world[1] - di->world_rect.ymin)*di->wo_to_sc[1];
403 }
404
405 void glaEnd2DDraw(gla2DDrawInfo *di)
406 {
407         glViewport(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
408         glScissor(di->orig_vp[0], di->orig_vp[1], di->orig_vp[2], di->orig_vp[3]);
409         glMatrixMode(GL_PROJECTION);
410         glLoadMatrixf(di->orig_projmat);
411         glMatrixMode(GL_MODELVIEW);
412         glLoadMatrixf(di->orig_viewmat);
413
414         MEM_freeN(di);
415 }