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