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