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