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