Ghost Context Refactor
[blender-staging.git] / source / gameengine / VideoTexture / Texture.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software  Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Copyright (c) 2007 The Zdeno Ash Miklas
19  *
20  * This source file is part of VideoTexture library
21  *
22  * Contributor(s):
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file gameengine/VideoTexture/Texture.cpp
28  *  \ingroup bgevideotex
29  */
30
31 // implementation
32
33 #include "PyObjectPlus.h"
34 #include <structmember.h>
35
36 #include "KX_GameObject.h"
37 #include "KX_Light.h"
38 #include "RAS_MeshObject.h"
39 #include "RAS_ILightObject.h"
40 #include "DNA_mesh_types.h"
41 #include "DNA_meshdata_types.h"
42 #include "DNA_image_types.h"
43 #include "IMB_imbuf_types.h"
44
45 #include "MEM_guardedalloc.h"
46
47 #include "KX_BlenderMaterial.h"
48 #include "BL_Texture.h"
49
50 #include "KX_KetsjiEngine.h"
51 #include "KX_PythonInit.h"
52 #include "Texture.h"
53 #include "ImageBase.h"
54 #include "Exception.h"
55
56 #include <memory.h>
57 #include "glew-mx.h"
58
59
60 // macro for exception handling and logging
61 #define CATCH_EXCP catch (Exception & exp) \
62 { exp.report(); return NULL; }
63
64
65 // Blender GameObject type
66 static BlendType<KX_GameObject> gameObjectType ("KX_GameObject");
67 static BlendType<KX_LightObject> lightObjectType ("KX_LightObject");
68
69
70 // load texture
71 void loadTexture(unsigned int texId, unsigned int *texture, short *size,
72                  bool mipmap)
73 {
74         // load texture for rendering
75         glBindTexture(GL_TEXTURE_2D, texId);
76         if (mipmap)
77         {
78                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
79                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
80                 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, texture);
81         } 
82         else
83         {
84                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
85                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
86                 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
87         }
88         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
89 }
90
91
92 // get pointer to material
93 RAS_IPolyMaterial * getMaterial (PyObject *obj, short matID)
94 {
95         // if object is available
96         if (obj != NULL)
97         {
98                 // get pointer to texture image
99                 KX_GameObject * gameObj = gameObjectType.checkType(obj);
100                 if (gameObj != NULL && gameObj->GetMeshCount() > 0)
101                 {
102                         // get material from mesh
103                         RAS_MeshObject * mesh = gameObj->GetMesh(0);
104                         RAS_MeshMaterial *meshMat = mesh->GetMeshMaterial(matID);
105                         if (meshMat != NULL && meshMat->m_bucket != NULL)
106                                 // return pointer to polygon or blender material
107                                 return meshMat->m_bucket->GetPolyMaterial();
108                 }
109         }
110         // otherwise material was not found
111         return NULL;
112 }
113
114 // get pointer to a lamp
115 static KX_LightObject *getLamp(PyObject *obj)
116 {
117         // if object is available
118         if (obj == NULL) return NULL;
119
120         // returns NULL if obj is not a KX_LightObject
121         return lightObjectType.checkType(obj);
122 }
123
124
125 // get material ID
126 short getMaterialID(PyObject *obj, const char *name)
127 {
128         // search for material
129         for (short matID = 0;; ++matID)
130         {
131                 // get material
132                 RAS_IPolyMaterial * mat = getMaterial(obj, matID);
133                 // if material is not available, report that no material was found
134                 if (mat == NULL) 
135                         break;
136                 // name is a material name if it starts with MA and a UV texture name if it starts with IM
137                 if (name[0] == 'I' && name[1] == 'M') {
138                         // if texture name matches
139                         if (strcmp(mat->GetTextureName().ReadPtr(), name) == 0)
140                                 return matID;
141                 }
142                 else {
143                         // if material name matches
144                         if (strcmp(mat->GetMaterialName().ReadPtr(), name) == 0)
145                                 return matID;
146                 }
147         }
148         // material was not found
149         return -1;
150 }
151
152
153 // Texture object allocation
154 static PyObject *Texture_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
155 {
156         // allocate object
157         Texture * self = reinterpret_cast<Texture*>(type->tp_alloc(type, 0));
158         // initialize object structure
159         self->m_actTex = 0;
160         self->m_orgSaved = false;
161         self->m_imgTexture = NULL;
162         self->m_matTexture = NULL;
163         self->m_mipmap = false;
164         self->m_scaledImg = NULL;
165         self->m_scaledImgSize = 0;
166         self->m_source = NULL;
167         self->m_lastClock = 0.0;
168         // return allocated object
169         return reinterpret_cast<PyObject*>(self);
170 }
171
172
173 // forward declaration
174 PyObject *Texture_close(Texture *self);
175 int Texture_setSource(Texture *self, PyObject *value, void *closure);
176
177
178 // Texture object deallocation
179 static void Texture_dealloc(Texture *self)
180 {
181         // release renderer
182         Py_XDECREF(self->m_source);
183         // close texture
184         PyObject *ret = Texture_close(self);
185         Py_DECREF(ret);
186         // release scaled image buffer
187         delete [] self->m_scaledImg;
188         // release object
189         Py_TYPE((PyObject *)self)->tp_free((PyObject *)self);
190 }
191
192
193 ExceptionID MaterialNotAvail;
194 ExpDesc MaterialNotAvailDesc(MaterialNotAvail, "Texture material is not available");
195
196 // Texture object initialization
197 static int Texture_init(Texture *self, PyObject *args, PyObject *kwds)
198 {
199         // parameters - game object with video texture
200         PyObject *obj = NULL;
201         // material ID
202         short matID = 0;
203         // texture ID
204         short texID = 0;
205         // texture object with shared texture ID
206         Texture * texObj = NULL;
207
208         static const char *kwlist[] = {"gameObj", "materialID", "textureID", "textureObj", NULL};
209
210         // get parameters
211         if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|hhO!",
212                 const_cast<char**>(kwlist), &obj, &matID, &texID, &TextureType,
213                 &texObj))
214                 return -1; 
215
216         // if parameters are available
217         if (obj != NULL)
218         {
219                 // process polygon material or blender material
220                 try
221                 {
222                         // get pointer to texture image
223                         RAS_IPolyMaterial * mat = getMaterial(obj, matID);
224                         KX_LightObject * lamp = getLamp(obj);
225                         if (mat != NULL)
226                         {
227                                 // is it blender material or polygon material
228                                 if (mat->GetFlag() & RAS_BLENDERGLSL) 
229                                 {
230                                         self->m_imgTexture = static_cast<KX_BlenderMaterial*>(mat)->getImage(texID);
231                                         self->m_useMatTexture = false;
232                                 } else
233                                 {
234                                         // get blender material texture
235                                         self->m_matTexture = static_cast<KX_BlenderMaterial*>(mat)->getTex(texID);
236                                         self->m_useMatTexture = true;
237                                 }
238                         }
239                         else if (lamp != NULL)
240                         {
241                                 self->m_imgTexture = lamp->GetLightData()->GetTextureImage(texID);
242                                 self->m_useMatTexture = false;
243                         }
244
245                         // check if texture is available, if not, initialization failed
246                         if (self->m_imgTexture == NULL && self->m_matTexture == NULL)
247                                 // throw exception if initialization failed
248                                 THRWEXCP(MaterialNotAvail, S_OK);
249
250                         // if texture object is provided
251                         if (texObj != NULL)
252                         {
253                                 // copy texture code
254                                 self->m_actTex = texObj->m_actTex;
255                                 self->m_mipmap = texObj->m_mipmap;
256                                 if (texObj->m_source != NULL)
257                                         Texture_setSource(self, reinterpret_cast<PyObject*>(texObj->m_source), NULL);
258                         }
259                         else
260                                 // otherwise generate texture code
261                                 glGenTextures(1, (GLuint*)&self->m_actTex);
262                 }
263                 catch (Exception & exp)
264                 {
265                         exp.report();
266                         return -1;
267                 }
268         }
269         // initialization succeded
270         return 0;
271 }
272
273
274 // close added texture
275 PyObject *Texture_close(Texture * self)
276 {
277         // restore texture
278         if (self->m_orgSaved)
279         {
280                 self->m_orgSaved = false;
281                 // restore original texture code
282                 if (self->m_useMatTexture)
283                         self->m_matTexture->swapTexture(self->m_orgTex);
284                 else
285                         self->m_imgTexture->bindcode = self->m_orgTex;
286                 // drop actual texture
287                 if (self->m_actTex != 0)
288                 {
289                         glDeleteTextures(1, (GLuint *)&self->m_actTex);
290                         self->m_actTex = 0;
291                 }
292         }
293         Py_RETURN_NONE;
294 }
295
296
297 // refresh texture
298 static PyObject *Texture_refresh(Texture *self, PyObject *args)
299 {
300         // get parameter - refresh source
301         PyObject *param;
302         double ts = -1.0;
303
304         if (!PyArg_ParseTuple(args, "O|d:refresh", &param, &ts) || !PyBool_Check(param))
305         {
306                 // report error
307                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
308                 return NULL;
309         }
310         // some trick here: we are in the business of loading a texture,
311         // no use to do it if we are still in the same rendering frame.
312         // We find this out by looking at the engine current clock time
313         KX_KetsjiEngine* engine = KX_GetActiveEngine();
314         if (engine->GetClockTime() != self->m_lastClock) 
315         {
316                 self->m_lastClock = engine->GetClockTime();
317                 // set source refresh
318                 bool refreshSource = (param == Py_True);
319                 // try to proces texture from source
320                 try
321                 {
322                         // if source is available
323                         if (self->m_source != NULL)
324                         {
325                                 // check texture code
326                                 if (!self->m_orgSaved)
327                                 {
328                                         self->m_orgSaved = true;
329                                         // save original image code
330                                         if (self->m_useMatTexture)
331                                                 self->m_orgTex = self->m_matTexture->swapTexture(self->m_actTex);
332                                         else
333                                         {
334                                                 self->m_orgTex = self->m_imgTexture->bindcode;
335                                                 self->m_imgTexture->bindcode = self->m_actTex;
336                                         }
337                                 }
338
339                                 // get texture
340                                 unsigned int * texture = self->m_source->m_image->getImage(self->m_actTex, ts);
341                                 // if texture is available
342                                 if (texture != NULL)
343                                 {
344                                         // get texture size
345                                         short * orgSize = self->m_source->m_image->getSize();
346                                         // calc scaled sizes
347                                         short size[2];
348                                         if (GLEW_ARB_texture_non_power_of_two)
349                                         {
350                                                 size[0] = orgSize[0];
351                                                 size[1] = orgSize[1];
352                                         }
353                                         else
354                                         {
355                                                 size[0] = ImageBase::calcSize(orgSize[0]);
356                                                 size[1] = ImageBase::calcSize(orgSize[1]);
357                                         }
358                                         // scale texture if needed
359                                         if (size[0] != orgSize[0] || size[1] != orgSize[1])
360                                         {
361                                                 // if scaled image buffer is smaller than needed
362                                                 if (self->m_scaledImgSize < (unsigned int)(size[0] * size[1]))
363                                                 {
364                                                         // new size
365                                                         self->m_scaledImgSize = size[0] * size[1];
366                                                         // allocate scaling image
367                                                         delete [] self->m_scaledImg;
368                                                         self->m_scaledImg = new unsigned int[self->m_scaledImgSize];
369                                                 }
370                                                 // scale texture
371                                                 gluScaleImage(GL_RGBA, orgSize[0], orgSize[1], GL_UNSIGNED_BYTE, texture,
372                                                         size[0], size[1], GL_UNSIGNED_BYTE, self->m_scaledImg);
373                                                 // use scaled image instead original
374                                                 texture = self->m_scaledImg;
375                                         }
376                                         // load texture for rendering
377                                         loadTexture(self->m_actTex, texture, size, self->m_mipmap);
378
379                                         // refresh texture source, if required
380                                         if (refreshSource) self->m_source->m_image->refresh();
381                                 }
382                         }
383                 }
384                 CATCH_EXCP;
385         }
386         Py_RETURN_NONE;
387 }
388
389 // get OpenGL Bind Id
390 static PyObject *Texture_getBindId(Texture *self, void *closure)
391 {
392         unsigned int id = self->m_actTex;
393         return Py_BuildValue("h", id);
394 }
395
396 // get mipmap value
397 static PyObject *Texture_getMipmap(Texture *self, void *closure)
398 {
399         // return true if flag is set, otherwise false
400         if (self->m_mipmap) Py_RETURN_TRUE;
401         else Py_RETURN_FALSE;
402 }
403
404 // set mipmap value
405 static int Texture_setMipmap(Texture *self, PyObject *value, void *closure)
406 {
407         // check parameter, report failure
408         if (value == NULL || !PyBool_Check(value))
409         {
410                 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
411                 return -1;
412         }
413         // set mipmap
414         self->m_mipmap = value == Py_True;
415         // success
416         return 0;
417 }
418
419
420 // get source object
421 static PyObject *Texture_getSource(Texture *self, PyObject *value, void *closure)
422 {
423         // if source exists
424         if (self->m_source != NULL)
425         {
426                 Py_INCREF(self->m_source);
427                 return reinterpret_cast<PyObject*>(self->m_source);
428         }
429         // otherwise return None
430         Py_RETURN_NONE;
431 }
432
433
434 // set source object
435 int Texture_setSource(Texture *self, PyObject *value, void *closure)
436 {
437         // check new value
438         if (value == NULL || !pyImageTypes.in(Py_TYPE(value)))
439         {
440                 // report value error
441                 PyErr_SetString(PyExc_TypeError, "Invalid type of value");
442                 return -1;
443         }
444         // increase ref count for new value
445         Py_INCREF(value);
446         // release previous
447         Py_XDECREF(self->m_source);
448         // set new value
449         self->m_source = reinterpret_cast<PyImage*>(value);
450         // return success
451         return 0;
452 }
453
454
455 // class Texture methods
456 static PyMethodDef textureMethods[] =
457 {
458         { "close", (PyCFunction)Texture_close, METH_NOARGS, "Close dynamic texture and restore original"},
459         { "refresh", (PyCFunction)Texture_refresh, METH_VARARGS, "Refresh texture from source"},
460         {NULL}  /* Sentinel */
461 };
462
463 // class Texture attributes
464 static PyGetSetDef textureGetSets[] =
465
466         {(char*)"source", (getter)Texture_getSource, (setter)Texture_setSource, (char*)"source of texture", NULL},
467         {(char*)"mipmap", (getter)Texture_getMipmap, (setter)Texture_setMipmap, (char*)"mipmap texture", NULL},
468         {(char*)"bindId", (getter)Texture_getBindId, NULL, (char*)"OpenGL Bind Name", NULL},
469         {NULL}
470 };
471
472
473 // class Texture declaration
474 PyTypeObject TextureType =
475 {
476         PyVarObject_HEAD_INIT(NULL, 0)
477         "VideoTexture.Texture",   /*tp_name*/
478         sizeof(Texture),           /*tp_basicsize*/
479         0,                         /*tp_itemsize*/
480         (destructor)Texture_dealloc,/*tp_dealloc*/
481         0,                         /*tp_print*/
482         0,                         /*tp_getattr*/
483         0,                         /*tp_setattr*/
484         0,                         /*tp_compare*/
485         0,                         /*tp_repr*/
486         0,                         /*tp_as_number*/
487         0,                         /*tp_as_sequence*/
488         0,                         /*tp_as_mapping*/
489         0,                         /*tp_hash */
490         0,                         /*tp_call*/
491         0,                         /*tp_str*/
492         0,                         /*tp_getattro*/
493         0,                         /*tp_setattro*/
494         &imageBufferProcs,         /*tp_as_buffer*/
495         Py_TPFLAGS_DEFAULT,        /*tp_flags*/
496         "Texture objects",       /* tp_doc */
497         0,                             /* tp_traverse */
498         0,                             /* tp_clear */
499         0,                             /* tp_richcompare */
500         0,                             /* tp_weaklistoffset */
501         0,                             /* tp_iter */
502         0,                             /* tp_iternext */
503         textureMethods,      /* tp_methods */
504         0,                   /* tp_members */
505         textureGetSets,            /* tp_getset */
506         0,                         /* tp_base */
507         0,                         /* tp_dict */
508         0,                         /* tp_descr_get */
509         0,                         /* tp_descr_set */
510         0,                         /* tp_dictoffset */
511         (initproc)Texture_init,    /* tp_init */
512         0,                         /* tp_alloc */
513         Texture_new,               /* tp_new */
514 };