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