forked from bartvdbraak/blender
df9d6737b9
The ultimate goal is to only allow the rasterizer to handle OpenGL and bf_gpu calls. This commit creates a RAS_ILightObject interface and a RAS_OpenGLLight implementation.
515 lines
14 KiB
C++
515 lines
14 KiB
C++
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright (c) 2007 The Zdeno Ash Miklas
|
|
*
|
|
* This source file is part of VideoTexture library
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file gameengine/VideoTexture/Texture.cpp
|
|
* \ingroup bgevideotex
|
|
*/
|
|
|
|
// implementation
|
|
|
|
#include "PyObjectPlus.h"
|
|
#include <structmember.h>
|
|
|
|
#include "KX_GameObject.h"
|
|
#include "KX_Light.h"
|
|
#include "RAS_MeshObject.h"
|
|
#include "RAS_ILightObject.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_image_types.h"
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "KX_BlenderMaterial.h"
|
|
#include "BL_Texture.h"
|
|
|
|
#include "KX_KetsjiEngine.h"
|
|
#include "KX_PythonInit.h"
|
|
#include "Texture.h"
|
|
#include "ImageBase.h"
|
|
#include "Exception.h"
|
|
|
|
#include <memory.h>
|
|
#include "GL/glew.h"
|
|
|
|
|
|
// macro for exception handling and logging
|
|
#define CATCH_EXCP catch (Exception & exp) \
|
|
{ exp.report(); return NULL; }
|
|
|
|
|
|
// Blender GameObject type
|
|
static BlendType<KX_GameObject> gameObjectType ("KX_GameObject");
|
|
static BlendType<KX_LightObject> lightObjectType ("KX_LightObject");
|
|
|
|
|
|
// load texture
|
|
void loadTexture(unsigned int texId, unsigned int *texture, short *size,
|
|
bool mipmap)
|
|
{
|
|
// load texture for rendering
|
|
glBindTexture(GL_TEXTURE_2D, texId);
|
|
if (mipmap)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, size[0], size[1], GL_RGBA, GL_UNSIGNED_BYTE, texture);
|
|
}
|
|
else
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, size[0], size[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, texture);
|
|
}
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
}
|
|
|
|
|
|
// get pointer to material
|
|
RAS_IPolyMaterial * getMaterial (PyObject *obj, short matID)
|
|
{
|
|
// if object is available
|
|
if (obj != NULL)
|
|
{
|
|
// get pointer to texture image
|
|
KX_GameObject * gameObj = gameObjectType.checkType(obj);
|
|
if (gameObj != NULL && gameObj->GetMeshCount() > 0)
|
|
{
|
|
// get material from mesh
|
|
RAS_MeshObject * mesh = gameObj->GetMesh(0);
|
|
RAS_MeshMaterial *meshMat = mesh->GetMeshMaterial(matID);
|
|
if (meshMat != NULL && meshMat->m_bucket != NULL)
|
|
// return pointer to polygon or blender material
|
|
return meshMat->m_bucket->GetPolyMaterial();
|
|
}
|
|
}
|
|
// otherwise material was not found
|
|
return NULL;
|
|
}
|
|
|
|
// get pointer to a lamp
|
|
static KX_LightObject *getLamp(PyObject *obj)
|
|
{
|
|
// if object is available
|
|
if (obj == NULL) return NULL;
|
|
|
|
// returns NULL if obj is not a KX_LightObject
|
|
return lightObjectType.checkType(obj);
|
|
}
|
|
|
|
|
|
// get material ID
|
|
short getMaterialID(PyObject *obj, const char *name)
|
|
{
|
|
// search for material
|
|
for (short matID = 0;; ++matID)
|
|
{
|
|
// get material
|
|
RAS_IPolyMaterial * mat = getMaterial(obj, matID);
|
|
// if material is not available, report that no material was found
|
|
if (mat == NULL)
|
|
break;
|
|
// name is a material name if it starts with MA and a UV texture name if it starts with IM
|
|
if (name[0] == 'I' && name[1] == 'M') {
|
|
// if texture name matches
|
|
if (strcmp(mat->GetTextureName().ReadPtr(), name) == 0)
|
|
return matID;
|
|
}
|
|
else {
|
|
// if material name matches
|
|
if (strcmp(mat->GetMaterialName().ReadPtr(), name) == 0)
|
|
return matID;
|
|
}
|
|
}
|
|
// material was not found
|
|
return -1;
|
|
}
|
|
|
|
|
|
// Texture object allocation
|
|
static PyObject *Texture_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
|
{
|
|
// allocate object
|
|
Texture * self = reinterpret_cast<Texture*>(type->tp_alloc(type, 0));
|
|
// initialize object structure
|
|
self->m_actTex = 0;
|
|
self->m_orgSaved = false;
|
|
self->m_imgTexture = NULL;
|
|
self->m_matTexture = NULL;
|
|
self->m_mipmap = false;
|
|
self->m_scaledImg = NULL;
|
|
self->m_scaledImgSize = 0;
|
|
self->m_source = NULL;
|
|
self->m_lastClock = 0.0;
|
|
// return allocated object
|
|
return reinterpret_cast<PyObject*>(self);
|
|
}
|
|
|
|
|
|
// forward declaration
|
|
PyObject *Texture_close(Texture *self);
|
|
int Texture_setSource(Texture *self, PyObject *value, void *closure);
|
|
|
|
|
|
// Texture object deallocation
|
|
static void Texture_dealloc(Texture *self)
|
|
{
|
|
// release renderer
|
|
Py_XDECREF(self->m_source);
|
|
// close texture
|
|
PyObject *ret = Texture_close(self);
|
|
Py_DECREF(ret);
|
|
// release scaled image buffer
|
|
delete [] self->m_scaledImg;
|
|
// release object
|
|
Py_TYPE((PyObject *)self)->tp_free((PyObject *)self);
|
|
}
|
|
|
|
|
|
ExceptionID MaterialNotAvail;
|
|
ExpDesc MaterialNotAvailDesc(MaterialNotAvail, "Texture material is not available");
|
|
|
|
// Texture object initialization
|
|
static int Texture_init(Texture *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
// parameters - game object with video texture
|
|
PyObject *obj = NULL;
|
|
// material ID
|
|
short matID = 0;
|
|
// texture ID
|
|
short texID = 0;
|
|
// texture object with shared texture ID
|
|
Texture * texObj = NULL;
|
|
|
|
static const char *kwlist[] = {"gameObj", "materialID", "textureID", "textureObj", NULL};
|
|
|
|
// get parameters
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|hhO!",
|
|
const_cast<char**>(kwlist), &obj, &matID, &texID, &TextureType,
|
|
&texObj))
|
|
return -1;
|
|
|
|
// if parameters are available
|
|
if (obj != NULL)
|
|
{
|
|
// process polygon material or blender material
|
|
try
|
|
{
|
|
// get pointer to texture image
|
|
RAS_IPolyMaterial * mat = getMaterial(obj, matID);
|
|
KX_LightObject * lamp = getLamp(obj);
|
|
if (mat != NULL)
|
|
{
|
|
// is it blender material or polygon material
|
|
if (mat->GetFlag() & RAS_BLENDERGLSL)
|
|
{
|
|
self->m_imgTexture = static_cast<KX_BlenderMaterial*>(mat)->getImage(texID);
|
|
self->m_useMatTexture = false;
|
|
} else
|
|
{
|
|
// get blender material texture
|
|
self->m_matTexture = static_cast<KX_BlenderMaterial*>(mat)->getTex(texID);
|
|
self->m_useMatTexture = true;
|
|
}
|
|
}
|
|
else if (lamp != NULL)
|
|
{
|
|
self->m_imgTexture = lamp->GetLightData()->GetTextureImage(texID);
|
|
self->m_useMatTexture = false;
|
|
}
|
|
|
|
// check if texture is available, if not, initialization failed
|
|
if (self->m_imgTexture == NULL && self->m_matTexture == NULL)
|
|
// throw exception if initialization failed
|
|
THRWEXCP(MaterialNotAvail, S_OK);
|
|
|
|
// if texture object is provided
|
|
if (texObj != NULL)
|
|
{
|
|
// copy texture code
|
|
self->m_actTex = texObj->m_actTex;
|
|
self->m_mipmap = texObj->m_mipmap;
|
|
if (texObj->m_source != NULL)
|
|
Texture_setSource(self, reinterpret_cast<PyObject*>(texObj->m_source), NULL);
|
|
}
|
|
else
|
|
// otherwise generate texture code
|
|
glGenTextures(1, (GLuint*)&self->m_actTex);
|
|
}
|
|
catch (Exception & exp)
|
|
{
|
|
exp.report();
|
|
return -1;
|
|
}
|
|
}
|
|
// initialization succeded
|
|
return 0;
|
|
}
|
|
|
|
|
|
// close added texture
|
|
PyObject *Texture_close(Texture * self)
|
|
{
|
|
// restore texture
|
|
if (self->m_orgSaved)
|
|
{
|
|
self->m_orgSaved = false;
|
|
// restore original texture code
|
|
if (self->m_useMatTexture)
|
|
self->m_matTexture->swapTexture(self->m_orgTex);
|
|
else
|
|
self->m_imgTexture->bindcode = self->m_orgTex;
|
|
// drop actual texture
|
|
if (self->m_actTex != 0)
|
|
{
|
|
glDeleteTextures(1, (GLuint *)&self->m_actTex);
|
|
self->m_actTex = 0;
|
|
}
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
// refresh texture
|
|
static PyObject *Texture_refresh(Texture *self, PyObject *args)
|
|
{
|
|
// get parameter - refresh source
|
|
PyObject *param;
|
|
double ts = -1.0;
|
|
|
|
if (!PyArg_ParseTuple(args, "O|d:refresh", ¶m, &ts) || !PyBool_Check(param))
|
|
{
|
|
// report error
|
|
PyErr_SetString(PyExc_TypeError, "The value must be a bool");
|
|
return NULL;
|
|
}
|
|
// some trick here: we are in the business of loading a texture,
|
|
// no use to do it if we are still in the same rendering frame.
|
|
// We find this out by looking at the engine current clock time
|
|
KX_KetsjiEngine* engine = KX_GetActiveEngine();
|
|
if (engine->GetClockTime() != self->m_lastClock)
|
|
{
|
|
self->m_lastClock = engine->GetClockTime();
|
|
// set source refresh
|
|
bool refreshSource = (param == Py_True);
|
|
// try to proces texture from source
|
|
try
|
|
{
|
|
// if source is available
|
|
if (self->m_source != NULL)
|
|
{
|
|
// check texture code
|
|
if (!self->m_orgSaved)
|
|
{
|
|
self->m_orgSaved = true;
|
|
// save original image code
|
|
if (self->m_useMatTexture)
|
|
self->m_orgTex = self->m_matTexture->swapTexture(self->m_actTex);
|
|
else
|
|
{
|
|
self->m_orgTex = self->m_imgTexture->bindcode;
|
|
self->m_imgTexture->bindcode = self->m_actTex;
|
|
}
|
|
}
|
|
|
|
// get texture
|
|
unsigned int * texture = self->m_source->m_image->getImage(self->m_actTex, ts);
|
|
// if texture is available
|
|
if (texture != NULL)
|
|
{
|
|
// get texture size
|
|
short * orgSize = self->m_source->m_image->getSize();
|
|
// calc scaled sizes
|
|
short size[2];
|
|
if (GLEW_ARB_texture_non_power_of_two)
|
|
{
|
|
size[0] = orgSize[0];
|
|
size[1] = orgSize[1];
|
|
}
|
|
else
|
|
{
|
|
size[0] = ImageBase::calcSize(orgSize[0]);
|
|
size[1] = ImageBase::calcSize(orgSize[1]);
|
|
}
|
|
// scale texture if needed
|
|
if (size[0] != orgSize[0] || size[1] != orgSize[1])
|
|
{
|
|
// if scaled image buffer is smaller than needed
|
|
if (self->m_scaledImgSize < (unsigned int)(size[0] * size[1]))
|
|
{
|
|
// new size
|
|
self->m_scaledImgSize = size[0] * size[1];
|
|
// allocate scaling image
|
|
delete [] self->m_scaledImg;
|
|
self->m_scaledImg = new unsigned int[self->m_scaledImgSize];
|
|
}
|
|
// scale texture
|
|
gluScaleImage(GL_RGBA, orgSize[0], orgSize[1], GL_UNSIGNED_BYTE, texture,
|
|
size[0], size[1], GL_UNSIGNED_BYTE, self->m_scaledImg);
|
|
// use scaled image instead original
|
|
texture = self->m_scaledImg;
|
|
}
|
|
// load texture for rendering
|
|
loadTexture(self->m_actTex, texture, size, self->m_mipmap);
|
|
|
|
// refresh texture source, if required
|
|
if (refreshSource) self->m_source->m_image->refresh();
|
|
}
|
|
}
|
|
}
|
|
CATCH_EXCP;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
// get OpenGL Bind Id
|
|
static PyObject *Texture_getBindId(Texture *self, void *closure)
|
|
{
|
|
unsigned int id = self->m_actTex;
|
|
return Py_BuildValue("h", id);
|
|
}
|
|
|
|
// get mipmap value
|
|
static PyObject *Texture_getMipmap(Texture *self, void *closure)
|
|
{
|
|
// return true if flag is set, otherwise false
|
|
if (self->m_mipmap) Py_RETURN_TRUE;
|
|
else Py_RETURN_FALSE;
|
|
}
|
|
|
|
// set mipmap value
|
|
static int Texture_setMipmap(Texture *self, PyObject *value, void *closure)
|
|
{
|
|
// check parameter, report failure
|
|
if (value == NULL || !PyBool_Check(value))
|
|
{
|
|
PyErr_SetString(PyExc_TypeError, "The value must be a bool");
|
|
return -1;
|
|
}
|
|
// set mipmap
|
|
self->m_mipmap = value == Py_True;
|
|
// success
|
|
return 0;
|
|
}
|
|
|
|
|
|
// get source object
|
|
static PyObject *Texture_getSource(Texture *self, PyObject *value, void *closure)
|
|
{
|
|
// if source exists
|
|
if (self->m_source != NULL)
|
|
{
|
|
Py_INCREF(self->m_source);
|
|
return reinterpret_cast<PyObject*>(self->m_source);
|
|
}
|
|
// otherwise return None
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
// set source object
|
|
int Texture_setSource(Texture *self, PyObject *value, void *closure)
|
|
{
|
|
// check new value
|
|
if (value == NULL || !pyImageTypes.in(Py_TYPE(value)))
|
|
{
|
|
// report value error
|
|
PyErr_SetString(PyExc_TypeError, "Invalid type of value");
|
|
return -1;
|
|
}
|
|
// increase ref count for new value
|
|
Py_INCREF(value);
|
|
// release previous
|
|
Py_XDECREF(self->m_source);
|
|
// set new value
|
|
self->m_source = reinterpret_cast<PyImage*>(value);
|
|
// return success
|
|
return 0;
|
|
}
|
|
|
|
|
|
// class Texture methods
|
|
static PyMethodDef textureMethods[] =
|
|
{
|
|
{ "close", (PyCFunction)Texture_close, METH_NOARGS, "Close dynamic texture and restore original"},
|
|
{ "refresh", (PyCFunction)Texture_refresh, METH_VARARGS, "Refresh texture from source"},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
// class Texture attributes
|
|
static PyGetSetDef textureGetSets[] =
|
|
{
|
|
{(char*)"source", (getter)Texture_getSource, (setter)Texture_setSource, (char*)"source of texture", NULL},
|
|
{(char*)"mipmap", (getter)Texture_getMipmap, (setter)Texture_setMipmap, (char*)"mipmap texture", NULL},
|
|
{(char*)"bindId", (getter)Texture_getBindId, NULL, (char*)"OpenGL Bind Name", NULL},
|
|
{NULL}
|
|
};
|
|
|
|
|
|
// class Texture declaration
|
|
PyTypeObject TextureType =
|
|
{
|
|
PyVarObject_HEAD_INIT(NULL, 0)
|
|
"VideoTexture.Texture", /*tp_name*/
|
|
sizeof(Texture), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
(destructor)Texture_dealloc,/*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
0, /*tp_getattr*/
|
|
0, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
0, /*tp_repr*/
|
|
0, /*tp_as_number*/
|
|
0, /*tp_as_sequence*/
|
|
0, /*tp_as_mapping*/
|
|
0, /*tp_hash */
|
|
0, /*tp_call*/
|
|
0, /*tp_str*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
&imageBufferProcs, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
"Texture objects", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
textureMethods, /* tp_methods */
|
|
0, /* tp_members */
|
|
textureGetSets, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)Texture_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
Texture_new, /* tp_new */
|
|
};
|