2 -----------------------------------------------------------------------------
3 This source file is part of VideoTexture library
5 Copyright (c) 2007 The Zdeno Ash Miklas
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
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.
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 -----------------------------------------------------------------------------
25 #include <PyObjectPlus.h>
26 #include <structmember.h>
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>
36 #include <MEM_guardedalloc.h>
38 #include <KX_BlenderMaterial.h>
39 #include <BL_Texture.h>
41 #include "KX_KetsjiEngine.h"
42 #include "KX_PythonInit.h"
44 #include "ImageBase.h"
45 #include "Exception.h"
51 // macro for exception handling and logging
52 #define CATCH_EXCP catch (Exception & exp) \
53 { exp.report(); return NULL; }
56 // Blender GameObject type
57 BlendType<KX_GameObject> gameObjectType ("KX_GameObject");
61 void loadTexture (unsigned int texId, unsigned int * texture, short * size,
64 // load texture for rendering
65 glBindTexture(GL_TEXTURE_2D, texId);
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);
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);
78 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
82 // get pointer to material
83 RAS_IPolyMaterial * getMaterial (PyObject *obj, short matID)
85 // if object is available
88 // get pointer to texture image
89 KX_GameObject * gameObj = gameObjectType.checkType(obj);
90 if (gameObj != NULL && gameObj->GetMeshCount() > 0)
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();
100 // otherwise material was not found
106 short getMaterialID (PyObject * obj, char * name)
108 // search for material
109 for (short matID = 0;; ++matID)
112 RAS_IPolyMaterial * mat = getMaterial(obj, matID);
113 // if material is not available, report that no material was found
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')
119 // if texture name matches
120 if (strcmp(mat->GetTextureName().ReadPtr(), name) == 0)
124 // if material name matches
125 if (strcmp(mat->GetMaterialName().ReadPtr(), name) == 0)
129 // material was not found
134 // Texture object allocation
135 PyObject * Texture_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
138 Texture * self = reinterpret_cast<Texture*>(type->tp_alloc(type, 0));
139 // initialize object structure
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);
154 // forward declaration
155 PyObject * Texture_close(Texture * self);
156 int Texture_setSource (Texture * self, PyObject * value, void * closure);
159 // Texture object deallocation
160 void Texture_dealloc (Texture * self)
163 Py_XDECREF(self->m_source);
165 PyObject* ret = Texture_close(self);
167 // release scaled image buffer
168 delete [] self->m_scaledImg;
170 ((PyObject *)self)->ob_type->tp_free((PyObject*)self);
174 ExceptionID MaterialNotAvail;
175 ExpDesc MaterialNotAvailDesc (MaterialNotAvail, "Texture material is not available");
177 // Texture object initialization
178 int Texture_init (Texture *self, PyObject *args, PyObject *kwds)
180 // parameters - game object with video texture
181 PyObject * obj = NULL;
186 // texture object with shared texture ID
187 Texture * texObj = NULL;
189 static const char *kwlist[] = {"gameObj", "materialID", "textureID", "textureObj", NULL};
192 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|hhO!",
193 const_cast<char**>(kwlist), &obj, &matID, &texID, &TextureType,
197 // if parameters are available
200 // process polygon material or blender material
203 // get pointer to texture image
204 RAS_IPolyMaterial * mat = getMaterial(obj, matID);
207 // is it blender material or polygon material
208 if (mat->GetFlag() & RAS_BLENDERGLSL)
210 self->m_imgTexture = static_cast<KX_BlenderMaterial*>(mat)->getImage(texID);
211 self->m_useMatTexture = false;
212 } else if (mat->GetFlag() & RAS_BLENDERMAT)
214 // get blender material texture
215 self->m_matTexture = static_cast<KX_BlenderMaterial*>(mat)->getTex(texID);
216 self->m_useMatTexture = true;
220 // get texture pointer from polygon material
221 MTFace * tface = static_cast<KX_PolygonMaterial*>(mat)->GetMTFace();
222 self->m_imgTexture = (Image*)tface->tpage;
223 self->m_useMatTexture = false;
226 // check if texture is available, if not, initialization failed
227 if (self->m_imgTexture == NULL && self->m_matTexture == NULL)
228 // throw exception if initialization failed
229 THRWEXCP(MaterialNotAvail, S_OK);
231 // if texture object is provided
235 self->m_actTex = texObj->m_actTex;
236 self->m_mipmap = texObj->m_mipmap;
237 if (texObj->m_source != NULL)
238 Texture_setSource(self, reinterpret_cast<PyObject*>(texObj->m_source), NULL);
241 // otherwise generate texture code
242 glGenTextures(1, (GLuint*)&self->m_actTex);
244 catch (Exception & exp)
250 // initialization succeded
255 // close added texture
256 PyObject * Texture_close(Texture * self)
259 if (self->m_orgSaved)
261 self->m_orgSaved = false;
262 // restore original texture code
263 if (self->m_useMatTexture)
264 self->m_matTexture->swapTexture(self->m_orgTex);
266 self->m_imgTexture->bindcode = self->m_orgTex;
267 // drop actual texture
268 if (self->m_actTex != 0)
270 glDeleteTextures(1, (GLuint *)&self->m_actTex);
279 PyObject * Texture_refresh (Texture * self, PyObject * args)
281 // get parameter - refresh source
285 if (!PyArg_ParseTuple(args, "O|d:refresh", ¶m, &ts) || !PyBool_Check(param))
288 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
291 // some trick here: we are in the business of loading a texture,
292 // no use to do it if we are still in the same rendering frame.
293 // We find this out by looking at the engine current clock time
294 KX_KetsjiEngine* engine = KX_GetActiveEngine();
295 if (engine->GetClockTime() != self->m_lastClock)
297 self->m_lastClock = engine->GetClockTime();
298 // set source refresh
299 bool refreshSource = (param == Py_True);
300 // try to proces texture from source
303 // if source is available
304 if (self->m_source != NULL)
306 // check texture code
307 if (!self->m_orgSaved)
309 self->m_orgSaved = true;
310 // save original image code
311 if (self->m_useMatTexture)
312 self->m_orgTex = self->m_matTexture->swapTexture(self->m_actTex);
315 self->m_orgTex = self->m_imgTexture->bindcode;
316 self->m_imgTexture->bindcode = self->m_actTex;
321 unsigned int * texture = self->m_source->m_image->getImage(self->m_actTex, ts);
322 // if texture is available
326 short * orgSize = self->m_source->m_image->getSize();
328 short size[] = {ImageBase::calcSize(orgSize[0]), ImageBase::calcSize(orgSize[1])};
329 // scale texture if needed
330 if (size[0] != orgSize[0] || size[1] != orgSize[1])
332 // if scaled image buffer is smaller than needed
333 if (self->m_scaledImgSize < (unsigned int)(size[0] * size[1]))
336 self->m_scaledImgSize = size[0] * size[1];
337 // allocate scaling image
338 delete [] self->m_scaledImg;
339 self->m_scaledImg = new unsigned int[self->m_scaledImgSize];
342 gluScaleImage(GL_RGBA, orgSize[0], orgSize[1], GL_UNSIGNED_BYTE, texture,
343 size[0], size[1], GL_UNSIGNED_BYTE, self->m_scaledImg);
344 // use scaled image instead original
345 texture = self->m_scaledImg;
347 // load texture for rendering
348 loadTexture (self->m_actTex, texture, size, self->m_mipmap);
350 // refresh texture source, if required
351 if (refreshSource) self->m_source->m_image->refresh();
362 PyObject * Texture_getMipmap (Texture * self, void * closure)
364 // return true if flag is set, otherwise false
365 if (self->m_mipmap) Py_RETURN_TRUE;
366 else Py_RETURN_FALSE;
370 int Texture_setMipmap (Texture * self, PyObject * value, void * closure)
372 // check parameter, report failure
373 if (value == NULL || !PyBool_Check(value))
375 PyErr_SetString(PyExc_TypeError, "The value must be a bool");
379 self->m_mipmap = value == Py_True;
386 PyObject * Texture_getSource (Texture * self, PyObject * value, void * closure)
389 if (self->m_source != NULL)
391 Py_INCREF(self->m_source);
392 return reinterpret_cast<PyObject*>(self->m_source);
394 // otherwise return None
400 int Texture_setSource (Texture * self, PyObject * value, void * closure)
403 if (value == NULL || !pyImageTypes.in(value->ob_type))
405 // report value error
406 PyErr_SetString(PyExc_TypeError, "Invalid type of value");
409 // increase ref count for new value
412 Py_XDECREF(self->m_source);
414 self->m_source = reinterpret_cast<PyImage*>(value);
420 // class Texture methods
421 static PyMethodDef textureMethods[] =
423 { "close", (PyCFunction)Texture_close, METH_NOARGS, "Close dynamic texture and restore original"},
424 { "refresh", (PyCFunction)Texture_refresh, METH_VARARGS, "Refresh texture from source"},
425 {NULL} /* Sentinel */
428 // class Texture attributes
429 static PyGetSetDef textureGetSets[] =
431 {(char*)"source", (getter)Texture_getSource, (setter)Texture_setSource, (char*)"source of texture", NULL},
432 {(char*)"mipmap", (getter)Texture_getMipmap, (setter)Texture_setMipmap, (char*)"mipmap texture", NULL},
437 // class Texture declaration
438 PyTypeObject TextureType =
440 PyVarObject_HEAD_INIT(NULL, 0)
441 "VideoTexture.Texture", /*tp_name*/
442 sizeof(Texture), /*tp_basicsize*/
444 (destructor)Texture_dealloc,/*tp_dealloc*/
451 0, /*tp_as_sequence*/
459 Py_TPFLAGS_DEFAULT, /*tp_flags*/
460 "Texture objects", /* tp_doc */
463 0, /* tp_richcompare */
464 0, /* tp_weaklistoffset */
467 textureMethods, /* tp_methods */
469 textureGetSets, /* tp_getset */
472 0, /* tp_descr_get */
473 0, /* tp_descr_set */
474 0, /* tp_dictoffset */
475 (initproc)Texture_init, /* tp_init */
477 Texture_new, /* tp_new */