BGE fix #20456 - 2.5: mouse position problem (offseted by 1 vertically)
[blender.git] / source / gameengine / GamePlayer / common / GPC_Canvas.cpp
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #ifndef NOPNG
35 #ifdef WIN32
36 #include "png.h"
37 #else
38 #include <png.h>
39 #endif
40 #endif // NOPNG
41
42 #include "RAS_IPolygonMaterial.h"
43 #include "GPC_Canvas.h"
44
45 GPC_Canvas::TBannerId GPC_Canvas::s_bannerId = 0;
46
47
48 GPC_Canvas::GPC_Canvas(
49         int width,
50         int height
51 ) : 
52         m_width(width),
53         m_height(height),
54         m_bannersEnabled(false)
55 {
56 }
57
58
59 GPC_Canvas::~GPC_Canvas()
60 {
61         DisposeAllBanners();
62 }
63
64
65 //  void GPC_Canvas::InitPostRenderingContext(void)
66 //  {
67 //      glViewport(0, 0, m_width, m_height);
68 //      glMatrixMode(GL_PROJECTION);
69 //      glLoadIdentity();
70         
71 //      glOrtho(-2.0, 2.0, -2.0, 2.0, -20.0, 20.0);
72
73 //      glMatrixMode(GL_MODELVIEW);
74 //      glLoadIdentity();
75
76 //      glEnable(GL_DEPTH_TEST);
77
78 //      glDepthFunc(GL_LESS);
79
80 //      glShadeModel(GL_SMOOTH);
81 //  }
82
83 void GPC_Canvas::Resize(int width, int height)
84 {
85         m_width = width;
86         m_height = height;
87 }
88
89 void GPC_Canvas::EndFrame()
90 {
91         if (m_bannersEnabled)
92                 DrawAllBanners();       
93 }
94
95
96 void GPC_Canvas::ClearColor(float r, float g, float b, float a)
97 {
98         ::glClearColor(r,g,b,a);
99 }
100
101 void GPC_Canvas::SetViewPort(int x1, int y1, int x2, int y2)
102 {
103                 /*      x1 and y1 are the min pixel coordinate (e.g. 0)
104                         x2 and y2 are the max pixel coordinate
105                         the width,height is calculated including both pixels
106                         therefore: max - min + 1
107                 */
108                 
109                 /* XXX, nasty, this needs to go somewhere else,
110                  * but where... definitly need to clean up this
111                  * whole canvas/rendertools mess.
112                  */
113         glEnable(GL_SCISSOR_TEST);
114
115         glViewport(x1,y1,x2-x1 + 1,y2-y1 + 1);
116         glScissor(x1,y1,x2-x1 + 1,y2-y1 + 1);
117 };
118
119
120 void GPC_Canvas::ClearBuffer(
121         int type
122 ){
123
124         int ogltype = 0;
125         if (type & RAS_ICanvas::COLOR_BUFFER )
126                 ogltype |= GL_COLOR_BUFFER_BIT;
127         if (type & RAS_ICanvas::DEPTH_BUFFER )
128                 ogltype |= GL_DEPTH_BUFFER_BIT;
129
130         ::glClear(ogltype);
131 }
132
133
134 GPC_Canvas::TBannerId GPC_Canvas::AddBanner(
135         unsigned int bannerWidth, unsigned int bannerHeight,
136         unsigned int imageWidth, unsigned int imageHeight,
137         unsigned char* imageData, 
138         TBannerAlignment alignment, bool enabled)
139 {
140         TBannerData banner;
141
142         banner.alignment = alignment;
143         banner.enabled = enabled;
144         banner.displayWidth = bannerWidth;
145         banner.displayHeight = bannerHeight;
146         banner.imageWidth = imageWidth;
147         banner.imageHeight = imageHeight;
148         unsigned int bannerDataSize = imageWidth*imageHeight*4;
149         banner.imageData = new unsigned char [bannerDataSize];
150         ::memcpy(banner.imageData, imageData, bannerDataSize);
151         banner.textureName = 0;
152
153         m_banners.insert(TBannerMap::value_type(++s_bannerId, banner));
154         return s_bannerId;
155 }
156
157
158 void GPC_Canvas::DisposeBanner(TBannerId id)
159 {
160         TBannerMap::iterator it = m_banners.find(id);
161         if (it != m_banners.end()) {
162                 DisposeBanner(it->second);
163                 m_banners.erase(it);
164         }
165 }
166
167 void GPC_Canvas::DisposeAllBanners()
168 {
169         TBannerMap::iterator it = m_banners.begin();
170         while (it != m_banners.end()) {
171                 DisposeBanner(it->second);
172                 it++;
173         }
174 }
175
176 void GPC_Canvas::SetBannerEnabled(TBannerId id, bool enabled)
177 {
178         TBannerMap::iterator it = m_banners.find(id);
179         if (it != m_banners.end()) {
180                 it->second.enabled = enabled;
181         }
182 }
183
184
185 void GPC_Canvas::SetBannerDisplayEnabled(bool enabled)
186 {
187         m_bannersEnabled = enabled;
188 }
189
190
191 void GPC_Canvas::DisposeBanner(TBannerData& banner)
192 {
193         if (banner.imageData) {
194                 delete [] banner.imageData;
195                 banner.imageData = 0;
196         }
197         if (banner.textureName) {
198                 ::glDeleteTextures(1, (GLuint*)&banner.textureName);
199         }
200 }
201
202 void GPC_Canvas::DrawAllBanners(void)
203 {
204         if(!m_bannersEnabled || (m_banners.size() < 1))
205                 return;
206         
207         // Save the old rendering parameters.
208
209         CanvasRenderState render_state;
210         PushRenderState(render_state);
211
212         // Set up everything for banner display.
213         
214         // Set up OpenGL matrices 
215         SetOrthoProjection();
216         // Activate OpenGL settings needed for display of the texture
217         ::glDisable(GL_LIGHTING);
218         ::glDisable(GL_DEPTH_TEST);
219         ::glDisable(GL_FOG);
220         ::glEnable(GL_TEXTURE_2D);
221         ::glEnable(GL_BLEND);
222         ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
223
224         TBannerMap::iterator it = m_banners.begin();
225         while (it != m_banners.end()) {
226                 if (it->second.enabled) {
227                         DrawBanner(it->second);
228                 }
229                 it++;
230         }
231
232         PopRenderState(render_state);
233 }
234
235
236 void GPC_Canvas::DrawBanner(TBannerData& banner)
237 {
238         if(!banner.enabled)
239                 return;
240
241         // Set up coordinates
242         int coords[4][2];
243         if (banner.alignment == alignTopLeft) {
244                 // Upper left
245                 coords[0][0] = 0;
246                 coords[0][1] = ((int)m_height)-banner.displayHeight;
247                 coords[1][0] = banner.displayWidth;
248                 coords[1][1] = ((int)m_height)-banner.displayHeight;
249                 coords[2][0] = banner.displayWidth;
250                 coords[2][1] = ((int)m_height);
251                 coords[3][0] = 0;
252                 coords[3][1] = ((int)m_height);
253         }
254         else {
255                 // Lower right
256                 coords[0][0] = (int)m_width - banner.displayWidth;
257                 coords[0][1] = 0;
258                 coords[1][0] = m_width;
259                 coords[1][1] = 0;
260                 coords[2][0] = m_width;
261                 coords[2][1] = banner.displayHeight;
262                 coords[3][0] = (int)m_width - banner.displayWidth;
263                 coords[3][1] = banner.displayHeight;
264         }
265         // Set up uvs
266         int uvs[4][2] = {
267                 { 0, 1},
268                 { 1, 1},
269                 { 1, 0},
270                 { 0, 0}
271         };
272
273         if (!banner.textureName) {
274                 ::glGenTextures(1, (GLuint*)&banner.textureName);
275                 ::glBindTexture(GL_TEXTURE_2D, banner.textureName);
276                 ::glTexImage2D(
277                         GL_TEXTURE_2D,                  // target
278                         0,                                              // level
279                         4,                                              // components
280                         banner.imageWidth,              // width
281                         banner.displayHeight,   // height
282                         0,                                              // border
283                         GL_RGBA,                                // format
284                         GL_UNSIGNED_BYTE,               // type
285                         banner.imageData);              // image data
286                 ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
287                 ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
288         }
289         else {
290                 ::glBindTexture(GL_TEXTURE_2D, banner.textureName);
291         }
292
293         // Draw the rectangle with the texture on it
294         ::glBegin(GL_QUADS);
295         ::glColor4f(1.f, 1.f, 1.f, 1.f);
296         ::glTexCoord2iv((GLint*)uvs[0]);
297         ::glVertex2iv((GLint*)coords[0]);
298         ::glTexCoord2iv((GLint*)uvs[1]);
299         ::glVertex2iv((GLint*)coords[1]);
300         ::glTexCoord2iv((GLint*)uvs[2]);
301         ::glVertex2iv((GLint*)coords[2]);
302         ::glTexCoord2iv((GLint*)uvs[3]);
303         ::glVertex2iv((GLint*)coords[3]);
304         ::glEnd();
305 }
306
307         void
308 GPC_Canvas::
309 PushRenderState(
310         CanvasRenderState & render_state
311 ){
312 #if 0
313
314         ::glMatrixMode(GL_PROJECTION);
315         ::glPushMatrix();
316         ::glMatrixMode(GL_MODELVIEW);
317         ::glPushMatrix();
318         ::glMatrixMode(GL_TEXTURE);
319         ::glPushMatrix();
320         // Save old OpenGL settings
321         ::glGetIntegerv(GL_LIGHTING, (GLint*)&(render_state.oldLighting));
322         ::glGetIntegerv(GL_DEPTH_TEST, (GLint*)&(render_state.oldDepthTest));
323         ::glGetIntegerv(GL_FOG, (GLint*)&(render_state.oldFog));
324         ::glGetIntegerv(GL_TEXTURE_2D, (GLint*)&(render_state.oldTexture2D));
325         ::glGetIntegerv(GL_BLEND, (GLint*)&(render_state.oldBlend));
326         ::glGetIntegerv(GL_BLEND_SRC, (GLint*)&(render_state.oldBlendSrc));
327         ::glGetIntegerv(GL_BLEND_DST, (GLint*)&(render_state.oldBlendDst));
328         ::glGetFloatv(GL_CURRENT_COLOR, render_state.oldColor);
329         ::glGetIntegerv(GL_DEPTH_WRITEMASK,(GLint*)&(render_state.oldWriteMask));
330 #else
331
332         glPushAttrib(GL_ALL_ATTRIB_BITS);
333
334 #endif
335 }
336
337         void
338 GPC_Canvas::
339 PopRenderState(
340         const CanvasRenderState & render_state
341 ){
342 #if 0
343         // Restore OpenGL settings
344         render_state.oldLighting ? ::glEnable(GL_LIGHTING) : glDisable(GL_LIGHTING);
345         render_state.oldDepthTest ? ::glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST);
346         render_state.oldFog ? ::glEnable(GL_FOG) : ::glDisable(GL_FOG);
347         render_state.oldTexture2D ? ::glEnable(GL_TEXTURE_2D) : glDisable(GL_TEXTURE_2D);
348         render_state.oldBlend ? glEnable(GL_BLEND) : ::glDisable(GL_BLEND);
349         ::glBlendFunc((GLenum)render_state.oldBlendSrc, (GLenum)render_state.oldBlendDst);
350         render_state.oldWriteMask ? ::glEnable(GL_DEPTH_WRITEMASK) : glDisable(GL_DEPTH_WRITEMASK);
351
352         ::glColor4fv(render_state.oldColor);
353         // Restore OpenGL matrices
354         ::glMatrixMode(GL_TEXTURE);
355         ::glPopMatrix();
356         ::glMatrixMode(GL_PROJECTION);
357         ::glPopMatrix();
358         ::glMatrixMode(GL_MODELVIEW);
359         ::glPopMatrix();
360
361 #else
362
363         glPopAttrib();
364 #endif
365 }
366
367         void
368 GPC_Canvas::
369 SetOrthoProjection(
370 ){
371         // Set up OpenGL matrices 
372         ::glViewport(0, 0, m_width, m_height);
373         ::glScissor(0, 0, m_width, m_height);
374         ::glMatrixMode(GL_PROJECTION);
375         ::glLoadIdentity();
376         ::glOrtho(0, m_width, 0, m_height, -1, 1);
377         ::glMatrixMode(GL_MODELVIEW);
378         ::glLoadIdentity();
379         ::glMatrixMode(GL_TEXTURE);
380         ::glLoadIdentity();
381 }
382
383         void
384 GPC_Canvas::
385 MakeScreenShot(
386         const char* filename
387 ){
388         png_structp png_ptr;
389         png_infop info_ptr;
390         unsigned char *pixels = 0;
391         png_bytepp row_pointers = 0;
392         int i, bytesperpixel = 3, color_type = PNG_COLOR_TYPE_RGB;
393         FILE *fp = 0;
394
395         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
396         if (!png_ptr) 
397         {
398                 std::cout << "Cannot png_create_write_struct." << std::endl;
399                 return;
400         }
401
402         info_ptr = png_create_info_struct(png_ptr);
403         if (!info_ptr) 
404         {
405                 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
406                 std::cout << "Cannot png_create_info_struct." << std::endl;
407                 return;
408         }
409
410         if (setjmp(png_jmpbuf(png_ptr))) {
411                 png_destroy_write_struct(&png_ptr, &info_ptr);
412                 delete [] pixels;
413                 delete [] row_pointers;
414                 // printf("Aborting\n");
415                 if (fp) {
416                         fflush(fp);
417                         fclose(fp);
418                 }
419                 return;
420         }
421
422         // copy image data
423
424         pixels = new unsigned char[GetWidth() * GetHeight() * bytesperpixel * sizeof(unsigned char)];
425         if (!pixels) {
426                 std::cout << "Cannot allocate pixels array" << std::endl;
427                 return;
428         }
429
430         glReadPixels(0, 0, GetWidth(), GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, pixels);
431
432         fp = fopen(filename, "wb");
433         if (!fp)
434         {
435                 std::cout << "Couldn't open " << filename << " for writing." << std::endl;
436                 longjmp(png_jmpbuf(png_ptr), 1);
437         }
438
439         png_init_io(png_ptr, fp);
440
441         /*
442         png_set_filter(png_ptr, 0,
443                 PNG_FILTER_NONE  | PNG_FILTER_VALUE_NONE |
444                 PNG_FILTER_SUB   | PNG_FILTER_VALUE_SUB  |
445                 PNG_FILTER_UP    | PNG_FILTER_VALUE_UP   |
446                 PNG_FILTER_AVG   | PNG_FILTER_VALUE_AVG  |
447                 PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH|
448                 PNG_ALL_FILTERS);
449
450         png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
451         */
452
453         // png image settings
454         png_set_IHDR(png_ptr,
455                  info_ptr,
456                  GetWidth(),
457                  GetHeight(),
458                  8,
459                  color_type,
460                  PNG_INTERLACE_NONE,
461                  PNG_COMPRESSION_TYPE_DEFAULT,
462                  PNG_FILTER_TYPE_DEFAULT);
463
464         // write the file header information
465         png_write_info(png_ptr, info_ptr);
466
467         // allocate memory for an array of row-pointers
468         row_pointers = new png_bytep [(GetHeight() * sizeof(png_bytep))];
469         if (!row_pointers) 
470         {
471                 std::cout << "Cannot allocate row-pointers array" << std::endl;
472                 longjmp(png_jmpbuf(png_ptr), 1);
473         }
474
475         // set the individual row-pointers to point at the correct offsets
476         for (i = 0; i < GetHeight(); i++) {
477                 row_pointers[GetHeight()-1-i] = (png_bytep)
478                         ((unsigned char *)pixels + (i * GetWidth()) * bytesperpixel * sizeof(unsigned char));
479         }
480
481         // write out the entire image data in one call
482         png_write_image(png_ptr, row_pointers);
483
484         // write the additional chunks to the PNG file (not really needed)
485         png_write_end(png_ptr, info_ptr);
486
487         // clean up
488         delete [] (pixels);
489         delete [] (row_pointers);
490         png_destroy_write_struct(&png_ptr, &info_ptr);
491
492         if (fp) 
493         {
494                 fflush(fp);
495                 fclose(fp);
496         }
497 }
498