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