BGE bug #29133: Render to Texture not working. Fixed by using same viewport to render...
[blender.git] / source / gameengine / VideoTexture / ImageViewport.cpp
1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of VideoTexture library
4
5 Copyright (c) 2007 The Zdeno Ash Miklas
6
7 This program is free software; you can redistribute it and/or modify it under
8 the terms of the GNU Lesser General Public License as published by the Free Software
9 Foundation; either version 2 of the License, or (at your option) any later
10 version.
11
12 This program is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License along with
17 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
19 http://www.gnu.org/copyleft/lesser.txt.
20 -----------------------------------------------------------------------------
21 */
22
23 /** \file gameengine/VideoTexture/ImageViewport.cpp
24  *  \ingroup bgevideotex
25  */
26
27 // implementation
28
29 #include <PyObjectPlus.h>
30 #include <structmember.h>
31
32 #include "GL/glew.h"
33
34 #include "KX_PythonInit.h"
35 #include "Texture.h"
36 #include "ImageBase.h"
37 #include "FilterSource.h"
38 #include "ImageViewport.h"
39
40
41 // constructor
42 ImageViewport::ImageViewport (void) : m_alpha(false), m_texInit(false)
43 {
44         // get viewport rectangle
45         RAS_Rect rect = KX_GetActiveEngine()->GetCanvas()->GetWindowArea();
46         m_viewport[0] = rect.GetLeft();
47         m_viewport[1] = rect.GetBottom();
48         m_viewport[2] = rect.GetWidth();
49         m_viewport[3] = rect.GetHeight();
50         
51         //glGetIntegerv(GL_VIEWPORT, m_viewport);
52         // create buffer for viewport image
53         m_viewportImage = new BYTE [4 * getViewportSize()[0] * getViewportSize()[1]];
54         // set attributes
55         setWhole(false);
56 }
57
58 // destructor
59 ImageViewport::~ImageViewport (void)
60 { delete m_viewportImage; }
61
62
63 // use whole viewport to capture image
64 void ImageViewport::setWhole (bool whole)
65 {
66         // set whole
67         m_whole = whole;
68         // set capture size to viewport size, if whole,
69         // otherwise place area in the middle of viewport
70         for (int idx = 0; idx < 2; ++idx)
71         {
72                 // capture size
73                 m_capSize[idx] = whole ? short(getViewportSize()[idx])
74                         : calcSize(short(getViewportSize()[idx]));
75                 // position
76                 m_position[idx] = whole ? 0 : ((getViewportSize()[idx] - m_capSize[idx]) >> 1);
77         }
78         // init image
79         init(m_capSize[0], m_capSize[1]);
80         // set capture position
81         setPosition();
82 }
83
84 void ImageViewport::setCaptureSize (short * size)
85 {
86         m_whole = false;
87         if (size == NULL) 
88                 size = m_capSize;
89         for (int idx = 0; idx < 2; ++idx)
90         {
91                 if (size[idx] < 1)
92                         m_capSize[idx] = 1;
93                 else if (size[idx] > getViewportSize()[idx])
94                         m_capSize[idx] = short(getViewportSize()[idx]);
95                 else
96                         m_capSize[idx] = size[idx];
97         }
98         init(m_capSize[0], m_capSize[1]);
99         // set capture position
100         setPosition();
101 }
102
103 // set position of capture rectangle
104 void ImageViewport::setPosition (GLint * pos)
105 {
106         // if new position is not provided, use existing position
107         if (pos == NULL) pos = m_position;
108         // save position
109         for (int idx = 0; idx < 2; ++idx)
110                 m_position[idx] = pos[idx] < 0 ? 0 : pos[idx] >= getViewportSize()[idx]
111                 - m_capSize[idx] ? getViewportSize()[idx] - m_capSize[idx] : pos[idx];
112         // recalc up left corner
113         for (int idx = 0; idx < 2; ++idx)
114                 m_upLeft[idx] = m_position[idx] + m_viewport[idx];
115 }
116
117
118 // capture image from viewport
119 void ImageViewport::calcImage (unsigned int texId, double ts)
120 {
121         // if scale was changed
122         if (m_scaleChange)
123                 // reset image
124                 init(m_capSize[0], m_capSize[1]);
125         // if texture wasn't initialized
126         if (!m_texInit)
127         {
128                 // initialize it
129                 loadTexture(texId, m_image, m_size);
130                 m_texInit = true;
131         }
132         // if texture can be directly created
133         if (texId != 0 && m_pyfilter == NULL && m_capSize[0] == calcSize(m_capSize[0])
134                 && m_capSize[1] == calcSize(m_capSize[1]) && !m_flip)
135         {
136                 // just copy current viewport to texture
137                 glBindTexture(GL_TEXTURE_2D, texId);
138                 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_upLeft[0], m_upLeft[1], (GLsizei)m_capSize[0], (GLsizei)m_capSize[1]);
139                 // image is not available
140                 m_avail = false;
141         }
142         // otherwise copy viewport to buffer, if image is not available
143         else if (!m_avail)
144         {
145                 // get frame buffer data
146                 if (m_alpha)
147                 {
148                         glReadPixels(m_upLeft[0], m_upLeft[1], (GLsizei)m_capSize[0], (GLsizei)m_capSize[1], GL_RGBA,
149                                      GL_UNSIGNED_BYTE, m_viewportImage);
150                         // filter loaded data
151                         FilterRGBA32 filt;
152                         filterImage(filt, m_viewportImage, m_capSize);
153                 }
154                 else
155                 {
156                         glReadPixels(m_upLeft[0], m_upLeft[1], (GLsizei)m_capSize[0], (GLsizei)m_capSize[1], GL_RGB,
157                                      GL_UNSIGNED_BYTE, m_viewportImage);
158                         // filter loaded data
159                         FilterRGB24 filt;
160                         filterImage(filt, m_viewportImage, m_capSize);
161                 }
162         }
163 }
164
165
166
167 // cast Image pointer to ImageViewport
168 inline ImageViewport * getImageViewport (PyImage * self)
169 { return static_cast<ImageViewport*>(self->m_image); }
170
171
172 // python methods
173
174
175 // get whole
176 PyObject * ImageViewport_getWhole (PyImage * self, void * closure)
177 {
178         if (self->m_image != NULL && getImageViewport(self)->getWhole()) Py_RETURN_TRUE;
179         else Py_RETURN_FALSE;
180 }
181
182 // set whole
183 int ImageViewport_setWhole (PyImage * self, PyObject * value, void * closure)
184 {
185         // check parameter, report failure
186         if (value == NULL || !PyBool_Check(value))
187         {
188                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
189                 return -1;
190         }
191         try
192         {
193                 // set whole, can throw in case of resize and buffer exports
194                 if (self->m_image != NULL) getImageViewport(self)->setWhole(value == Py_True);
195         }
196         catch (Exception & exp)
197         {
198                 exp.report();
199                 return -1;
200         }
201         // success
202         return 0;
203 }
204
205 // get alpha
206 PyObject * ImageViewport_getAlpha (PyImage * self, void * closure)
207 {
208         if (self->m_image != NULL && getImageViewport(self)->getAlpha()) Py_RETURN_TRUE;
209         else Py_RETURN_FALSE;
210 }
211
212 // set whole
213 int ImageViewport_setAlpha (PyImage * self, PyObject * value, void * closure)
214 {
215         // check parameter, report failure
216         if (value == NULL || !PyBool_Check(value))
217         {
218                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
219                 return -1;
220         }
221         // set alpha
222         if (self->m_image != NULL) getImageViewport(self)->setAlpha(value == Py_True);
223         // success
224         return 0;
225 }
226
227
228 // get position
229 static PyObject * ImageViewport_getPosition (PyImage * self, void * closure)
230 {
231         return Py_BuildValue("(ii)", getImageViewport(self)->getPosition()[0],
232                 getImageViewport(self)->getPosition()[1]);
233 }
234
235 // set position
236 static int ImageViewport_setPosition (PyImage * self, PyObject * value, void * closure)
237 {
238         // check validity of parameter
239         if (value == NULL || !PySequence_Check(value) || PySequence_Size(value) != 2
240                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 0))
241                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 1)))
242         {
243                 PyErr_SetString(PyExc_TypeError, "The value must be a sequence of 2 ints");
244                 return -1;
245         }
246         // set position
247         GLint pos [] = {
248                 GLint(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 0))),
249                         GLint(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 1)))
250         };
251         getImageViewport(self)->setPosition(pos);
252         // success
253         return 0;
254 }
255
256 // get capture size
257 PyObject * ImageViewport_getCaptureSize (PyImage * self, void * closure)
258 {
259         return Py_BuildValue("(ii)", getImageViewport(self)->getCaptureSize()[0],
260                 getImageViewport(self)->getCaptureSize()[1]);
261 }
262
263 // set capture size
264 int ImageViewport_setCaptureSize (PyImage * self, PyObject * value, void * closure)
265 {
266         // check validity of parameter
267         if (value == NULL || !PySequence_Check(value) || PySequence_Size(value) != 2
268                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 0))
269                 || !PyLong_Check(PySequence_Fast_GET_ITEM(value, 1)))
270         {
271                 PyErr_SetString(PyExc_TypeError, "The value must be a sequence of 2 ints");
272                 return -1;
273         }
274         // set capture size
275         short size [] = {
276                 short(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 0))),
277                         short(PyLong_AsSsize_t(PySequence_Fast_GET_ITEM(value, 1)))
278         };
279         try
280         {
281                 // can throw in case of resize and buffer exports
282                 getImageViewport(self)->setCaptureSize(size);
283         }
284         catch (Exception & exp)
285         {
286                 exp.report();
287                 return -1;
288         }
289         // success
290         return 0;
291 }
292
293
294 // methods structure
295 static PyMethodDef imageViewportMethods[] =
296 { // methods from ImageBase class
297         {"refresh", (PyCFunction)Image_refresh, METH_NOARGS, "Refresh image - invalidate its current content"},
298         {NULL}
299 };
300 // attributes structure
301 static PyGetSetDef imageViewportGetSets[] =
302
303         {(char*)"whole", (getter)ImageViewport_getWhole, (setter)ImageViewport_setWhole, (char*)"use whole viewport to capture", NULL},
304         {(char*)"position", (getter)ImageViewport_getPosition, (setter)ImageViewport_setPosition, (char*)"upper left corner of captured area", NULL},
305         {(char*)"capsize", (getter)ImageViewport_getCaptureSize, (setter)ImageViewport_setCaptureSize, (char*)"size of viewport area being captured", NULL},
306         {(char*)"alpha", (getter)ImageViewport_getAlpha, (setter)ImageViewport_setAlpha, (char*)"use alpha in texture", NULL},
307         // attributes from ImageBase class
308         {(char*)"valid", (getter)Image_valid, NULL, (char*)"bool to tell if an image is available", NULL},
309         {(char*)"image", (getter)Image_getImage, NULL, (char*)"image data", NULL},
310         {(char*)"size", (getter)Image_getSize, NULL, (char*)"image size", NULL},
311         {(char*)"scale", (getter)Image_getScale, (setter)Image_setScale, (char*)"fast scale of image (near neighbour)", NULL},
312         {(char*)"flip", (getter)Image_getFlip, (setter)Image_setFlip, (char*)"flip image vertically", NULL},
313         {(char*)"filter", (getter)Image_getFilter, (setter)Image_setFilter, (char*)"pixel filter", NULL},
314         {NULL}
315 };
316
317
318 // define python type
319 PyTypeObject ImageViewportType =
320
321         PyVarObject_HEAD_INIT(NULL, 0)
322         "VideoTexture.ImageViewport",   /*tp_name*/
323         sizeof(PyImage),          /*tp_basicsize*/
324         0,                         /*tp_itemsize*/
325         (destructor)Image_dealloc, /*tp_dealloc*/
326         0,                         /*tp_print*/
327         0,                         /*tp_getattr*/
328         0,                         /*tp_setattr*/
329         0,                         /*tp_compare*/
330         0,                         /*tp_repr*/
331         0,                         /*tp_as_number*/
332         0,                         /*tp_as_sequence*/
333         0,                         /*tp_as_mapping*/
334         0,                         /*tp_hash */
335         0,                         /*tp_call*/
336         0,                         /*tp_str*/
337         0,                         /*tp_getattro*/
338         0,                         /*tp_setattro*/
339         &imageBufferProcs,         /*tp_as_buffer*/
340         Py_TPFLAGS_DEFAULT,        /*tp_flags*/
341         "Image source from viewport",       /* tp_doc */
342         0,                             /* tp_traverse */
343         0,                             /* tp_clear */
344         0,                             /* tp_richcompare */
345         0,                             /* tp_weaklistoffset */
346         0,                             /* tp_iter */
347         0,                             /* tp_iternext */
348         imageViewportMethods,    /* tp_methods */
349         0,                   /* tp_members */
350         imageViewportGetSets,          /* tp_getset */
351         0,                         /* tp_base */
352         0,                         /* tp_dict */
353         0,                         /* tp_descr_get */
354         0,                         /* tp_descr_set */
355         0,                         /* tp_dictoffset */
356         (initproc)Image_init<ImageViewport>,     /* tp_init */
357         0,                         /* tp_alloc */
358         Image_allocNew,           /* tp_new */
359 };