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