mirror of https://github.com/H-uru/korman.git
Adam Johnson
9 years ago
13 changed files with 723 additions and 100 deletions
@ -0,0 +1,38 @@ |
|||||||
|
project(korlib) |
||||||
|
cmake_minimum_required(VERSION 3.0) |
||||||
|
|
||||||
|
# Stolen shamelessly from PyHSPlasma |
||||||
|
find_package(PythonLibs REQUIRED) |
||||||
|
find_package(PythonInterp "${PYTHONLIBS_VERSION_STRING}" REQUIRED) |
||||||
|
# make sure the versions match |
||||||
|
if (NOT "${PYTHONLIBS_VERSION_STRING}" STREQUAL "${PYTHON_VERSION_STRING}") |
||||||
|
message(FATAL_ERROR "Versions of Python libraries (${PYTHONLIBS_VERSION_STRING}) and Python interpreter (${PYTHON_VERSION_STRING}) do not match. Please configure the paths manually.") |
||||||
|
endif() |
||||||
|
|
||||||
|
find_package(HSPlasma REQUIRED) |
||||||
|
find_package(OpenGL REQUIRED) |
||||||
|
|
||||||
|
# Da files |
||||||
|
set(korlib_HEADERS |
||||||
|
buffer.h |
||||||
|
korlib.h |
||||||
|
texture.h |
||||||
|
) |
||||||
|
|
||||||
|
set(korlib_SOURCES |
||||||
|
buffer.cpp |
||||||
|
module.cpp |
||||||
|
texture.cpp |
||||||
|
) |
||||||
|
|
||||||
|
include_directories(${HSPlasma_INCLUDE_DIRS}) |
||||||
|
include_directories(${OPENGL_INCLUDE_DIR}) |
||||||
|
include_directories(${PYTHON_INCLUDE_DIRS}) |
||||||
|
|
||||||
|
add_library(_korlib SHARED ${korlib_HEADERS} ${korlib_SOURCES}) |
||||||
|
if(NOT WIN32) |
||||||
|
set_target_properties(_korlib PROPERTIES SUFFIX ".so") |
||||||
|
else() |
||||||
|
set_target_properties(_korlib PROPERTIES SUFFIX ".pyd") |
||||||
|
endif() |
||||||
|
target_link_libraries(_korlib HSPlasma ${OPENGL_LIBRARIES} ${PYTHON_LIBRARIES}) |
@ -0,0 +1,111 @@ |
|||||||
|
/* This file is part of Korman.
|
||||||
|
* |
||||||
|
* Korman 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 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "buffer.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
|
||||||
|
static void pyBuffer_dealloc(pyBuffer* self) { |
||||||
|
delete[] self->m_buffer; |
||||||
|
Py_TYPE(self)->tp_free((PyObject*)self); |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyBuffer_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { |
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Buffers cannot be created by mere mortals"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
PyTypeObject pyBuffer_Type = { |
||||||
|
PyVarObject_HEAD_INIT(NULL, 0) |
||||||
|
"_korlib.Buffer", /* tp_name */ |
||||||
|
sizeof(pyBuffer), /* tp_basicsize */ |
||||||
|
0, /* tp_itemsize */ |
||||||
|
|
||||||
|
(destructor)pyBuffer_dealloc, /* tp_dealloc */ |
||||||
|
NULL, /* tp_print */ |
||||||
|
NULL, /* tp_getattr */ |
||||||
|
NULL, /* tp_setattr */ |
||||||
|
NULL, /* tp_compare */ |
||||||
|
NULL, /* tp_repr */ |
||||||
|
NULL, /* tp_as_number */ |
||||||
|
NULL, /* tp_as_sequence */ |
||||||
|
NULL, /* tp_as_mapping */ |
||||||
|
NULL, /* tp_hash */ |
||||||
|
NULL, /* tp_call */ |
||||||
|
NULL, /* tp_str */ |
||||||
|
NULL, /* tp_getattro */ |
||||||
|
NULL, /* tp_setattro */ |
||||||
|
NULL, /* tp_as_buffer */ |
||||||
|
|
||||||
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
||||||
|
"Buffer", /* tp_doc */ |
||||||
|
|
||||||
|
NULL, /* tp_traverse */ |
||||||
|
NULL, /* tp_clear */ |
||||||
|
NULL, /* tp_richcompare */ |
||||||
|
0, /* tp_weaklistoffset */ |
||||||
|
NULL, /* tp_iter */ |
||||||
|
NULL, /* tp_iternext */ |
||||||
|
|
||||||
|
NULL, /* tp_methods */ |
||||||
|
NULL, /* tp_members */ |
||||||
|
NULL, /* tp_getset */ |
||||||
|
NULL, /* tp_base */ |
||||||
|
NULL, /* tp_dict */ |
||||||
|
NULL, /* tp_descr_get */ |
||||||
|
NULL, /* tp_descr_set */ |
||||||
|
0, /* tp_dictoffset */ |
||||||
|
|
||||||
|
NULL, /* tp_init */ |
||||||
|
NULL, /* tp_alloc */ |
||||||
|
pyBuffer_new, /* tp_new */ |
||||||
|
NULL, /* tp_free */ |
||||||
|
NULL, /* tp_is_gc */ |
||||||
|
|
||||||
|
NULL, /* tp_bases */ |
||||||
|
NULL, /* tp_mro */ |
||||||
|
NULL, /* tp_cache */ |
||||||
|
NULL, /* tp_subclasses */ |
||||||
|
NULL, /* tp_weaklist */ |
||||||
|
|
||||||
|
NULL, /* tp_del */ |
||||||
|
0, /* tp_version_tag */ |
||||||
|
NULL, /* tp_finalize */ |
||||||
|
}; |
||||||
|
|
||||||
|
PyObject* Init_pyBuffer_Type() { |
||||||
|
if (PyType_Ready(&pyBuffer_Type) < 0) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
Py_INCREF(&pyBuffer_Type); |
||||||
|
return (PyObject*)&pyBuffer_Type; |
||||||
|
} |
||||||
|
|
||||||
|
int pyBuffer_Check(PyObject* obj) { |
||||||
|
if (obj->ob_type == &pyBuffer_Type |
||||||
|
|| PyType_IsSubtype(obj->ob_type, &pyBuffer_Type)) |
||||||
|
return 1; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
PyObject* pyBuffer_Steal(uint8_t* buffer, size_t size) { |
||||||
|
pyBuffer* obj = PyObject_New(pyBuffer, &pyBuffer_Type); |
||||||
|
obj->m_buffer = buffer; |
||||||
|
obj->m_size = size; |
||||||
|
return (PyObject*)obj; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,37 @@ |
|||||||
|
/* This file is part of Korman.
|
||||||
|
* |
||||||
|
* Korman 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 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _KORLIB_BUFFER_H |
||||||
|
#define _KORLIB_BUFFER_H |
||||||
|
|
||||||
|
#include "korlib.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
PyObject_HEAD |
||||||
|
uint8_t* m_buffer; |
||||||
|
size_t m_size; |
||||||
|
} pyBuffer; |
||||||
|
|
||||||
|
extern PyTypeObject pyBuffer_Type; |
||||||
|
PyObject* Init_pyBuffer_Type(); |
||||||
|
int pyBuffer_Check(PyObject*); |
||||||
|
PyObject* pyBuffer_Steal(uint8_t*, size_t); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
#endif // _KORLIB_BUFFER_H
|
@ -0,0 +1,35 @@ |
|||||||
|
/* This file is part of Korman.
|
||||||
|
* |
||||||
|
* Korman 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 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _KORLIB_H |
||||||
|
#define _KORLIB_H |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <Python.h> |
||||||
|
|
||||||
|
#define _pycs(x) const_cast<char*>(x) |
||||||
|
|
||||||
|
class PyObjectRef { |
||||||
|
PyObject* m_object; |
||||||
|
|
||||||
|
public: |
||||||
|
PyObjectRef(PyObject* o) : m_object(o) { } |
||||||
|
~PyObjectRef() { if (m_object) Py_DECREF(m_object); } |
||||||
|
|
||||||
|
operator PyObject*() const { return m_object; } |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _KORLIB_H
|
@ -0,0 +1,45 @@ |
|||||||
|
/* This file is part of Korman.
|
||||||
|
* |
||||||
|
* Korman 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 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "buffer.h" |
||||||
|
#include "texture.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
|
||||||
|
static PyModuleDef korlib_Module = { |
||||||
|
PyModuleDef_HEAD_INIT, /* m_base */ |
||||||
|
"_korlib", /* m_name */ |
||||||
|
"C++ korlib implementation",/* m_doc */ |
||||||
|
0, /* m_size */ |
||||||
|
NULL, /* m_methods */ |
||||||
|
NULL, /* m_reload */ |
||||||
|
NULL, /* m_traverse */ |
||||||
|
NULL, /* m_clear */ |
||||||
|
NULL, /* m_free */ |
||||||
|
}; |
||||||
|
|
||||||
|
PyMODINIT_FUNC PyInit__korlib() { |
||||||
|
PyObject* module = PyModule_Create(&korlib_Module); |
||||||
|
|
||||||
|
// Module classes...
|
||||||
|
PyModule_AddObject(module, "Buffer", Init_pyBuffer_Type()); |
||||||
|
PyModule_AddObject(module, "GLTexture", Init_pyGLTexture_Type()); |
||||||
|
|
||||||
|
return module; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
@ -0,0 +1,289 @@ |
|||||||
|
/* This file is part of Korman.
|
||||||
|
* |
||||||
|
* Korman 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 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "texture.h" |
||||||
|
#include "buffer.h" |
||||||
|
|
||||||
|
#ifdef _WIN32 |
||||||
|
# define WIN32_LEAN_AND_MEAN |
||||||
|
# define NOMINMAX |
||||||
|
# include <windows.h> |
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#include <gl/gl.h> |
||||||
|
#include <PRP/Surface/plMipmap.h> |
||||||
|
|
||||||
|
#ifndef GL_GENERATE_MIPMAP |
||||||
|
# define GL_GENERATE_MIPMAP 0x8191 |
||||||
|
#endif // GL_GENERATE_MIPMAP
|
||||||
|
|
||||||
|
extern "C" { |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
PyObject_HEAD |
||||||
|
PyObject* m_blenderImage; |
||||||
|
bool m_ownIt; |
||||||
|
GLint m_prevImage; |
||||||
|
bool m_changedState; |
||||||
|
GLint m_mipmapState; |
||||||
|
} pyGLTexture; |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
PyObject_HEAD |
||||||
|
plMipmap* fThis; |
||||||
|
bool fPyOwned; |
||||||
|
} pyMipmap; |
||||||
|
|
||||||
|
static void pyGLTexture_dealloc(pyGLTexture* self) { |
||||||
|
if (self->m_blenderImage) Py_DECREF(self->m_blenderImage); |
||||||
|
Py_TYPE(self)->tp_free((PyObject*)self); |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyGLTexture_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { |
||||||
|
pyGLTexture* self = (pyGLTexture*)type->tp_alloc(type, 0); |
||||||
|
self->m_blenderImage = NULL; |
||||||
|
self->m_ownIt = false; |
||||||
|
self->m_prevImage = 0; |
||||||
|
self->m_changedState = false; |
||||||
|
self->m_mipmapState = 0; |
||||||
|
return (PyObject*)self; |
||||||
|
} |
||||||
|
|
||||||
|
static int pyGLTexture___init__(pyGLTexture* self, PyObject* args, PyObject* kwds) { |
||||||
|
PyObject* blender_image; |
||||||
|
if (!PyArg_ParseTuple(args, "O", &blender_image)) { |
||||||
|
PyErr_SetString(PyExc_TypeError, "expected a bpy.types.Image"); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
// Save a reference to the Blender image
|
||||||
|
Py_INCREF(blender_image); |
||||||
|
self->m_blenderImage = blender_image; |
||||||
|
|
||||||
|
// Done!
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyGLTexture__enter__(pyGLTexture* self) { |
||||||
|
// Is the image already loaded?
|
||||||
|
PyObjectRef bindcode = PyObject_GetAttrString(self->m_blenderImage, "bindcode"); |
||||||
|
if (!PyLong_Check(bindcode)) { |
||||||
|
PyErr_SetString(PyExc_RuntimeError, "Image bindcode isn't a long?"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
glGetIntegerv(GL_TEXTURE_BINDING_2D, &self->m_prevImage); |
||||||
|
GLuint image_bindcode = PyLong_AsLong(bindcode); |
||||||
|
self->m_ownIt = image_bindcode == 0; |
||||||
|
|
||||||
|
// Load image into GL
|
||||||
|
if (self->m_ownIt) { |
||||||
|
PyObjectRef new_bind = PyObject_CallMethod(self->m_blenderImage, "gl_load", NULL); |
||||||
|
if (!(PyObject*)new_bind) { |
||||||
|
PyErr_SetString(PyExc_RuntimeError, "failed to load image into GL"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
image_bindcode = PyLong_AsLong(new_bind); |
||||||
|
} |
||||||
|
|
||||||
|
// Set image as current in GL
|
||||||
|
if (self->m_prevImage != image_bindcode) { |
||||||
|
self->m_changedState = true; |
||||||
|
glBindTexture(GL_TEXTURE_2D, image_bindcode); |
||||||
|
} |
||||||
|
|
||||||
|
// Misc GL state
|
||||||
|
glGetTexParameteriv(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, &self->m_mipmapState); |
||||||
|
|
||||||
|
Py_INCREF(self); |
||||||
|
return (PyObject*)self; |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyGLTexture__exit__(pyGLTexture* self, PyObject*) { |
||||||
|
// We don't care about the args here
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, self->m_mipmapState); |
||||||
|
if (self->m_changedState) |
||||||
|
glBindTexture(GL_TEXTURE_2D, self->m_prevImage); |
||||||
|
Py_RETURN_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyGLTexture_generate_mipmap(pyGLTexture* self) { |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1); |
||||||
|
Py_RETURN_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static uint8_t* _get_level_data(pyGLTexture* self, GLint level, bool bgra, size_t* size) { |
||||||
|
GLint width, height; |
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_WIDTH, &width); |
||||||
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, level, GL_TEXTURE_HEIGHT, &height); |
||||||
|
GLenum fmt = bgra ? GL_BGRA_EXT : GL_RGBA; |
||||||
|
|
||||||
|
*size = (width * height * 4); |
||||||
|
uint8_t* buf = new uint8_t[*size]; |
||||||
|
glGetTexImage(GL_TEXTURE_2D, level, fmt, GL_UNSIGNED_BYTE, reinterpret_cast<GLvoid*>(buf)); |
||||||
|
return buf; |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyGLTexture_get_level_data(pyGLTexture* self, PyObject* args, PyObject* kwargs) { |
||||||
|
static char* kwlist[] = { _pycs("level"), _pycs("calc_alpha"), _pycs("bgra"), |
||||||
|
_pycs("quiet"), NULL }; |
||||||
|
GLint level = 0; |
||||||
|
bool calc_alpha = false; |
||||||
|
bool bgra = false; |
||||||
|
bool quiet = false; |
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ibbb", kwlist, &level, &calc_alpha, &bgra, &quiet)) { |
||||||
|
PyErr_SetString(PyExc_TypeError, "get_level_data expects an optional int, bool, bool, bool"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
size_t bufsz; |
||||||
|
uint8_t* buf = _get_level_data(self, level, bgra, &bufsz); |
||||||
|
if (calc_alpha) { |
||||||
|
for (size_t i = 0; i < bufsz; i += 4) |
||||||
|
buf[i + 3] = (buf[i + 0] + buf[i + 1] + buf[i + 2]) / 3; |
||||||
|
} |
||||||
|
|
||||||
|
return pyBuffer_Steal(buf, bufsz); |
||||||
|
} |
||||||
|
|
||||||
|
static PyObject* pyGLTexture_store_in_mipmap(pyGLTexture* self, PyObject* args) { |
||||||
|
pyMipmap* pymipmap; |
||||||
|
PyObject* levels; |
||||||
|
size_t compression; |
||||||
|
if (!PyArg_ParseTuple(args, "OOn", &pymipmap, &levels, &compression) || !PySequence_Check(levels)) { |
||||||
|
PyErr_SetString(PyExc_TypeError, "store_in_mipmap expects a plMipmap, sequence of Buffer and int"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// Since we actually have no way of knowing if that really is a pyMipmap...
|
||||||
|
plMipmap* mipmap = plMipmap::Convert(pymipmap->fThis, false); |
||||||
|
if (!mipmap) { |
||||||
|
PyErr_SetString(PyExc_TypeError, "store_in_mipmap expects a plMipmap, sequence of Buffer and int"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
for (Py_ssize_t i = 0; i < PySequence_Size(levels); ++i) { |
||||||
|
pyBuffer* item = (pyBuffer*)PySequence_GetItem(levels, i); |
||||||
|
if (!pyBuffer_Check((PyObject*)item)) { |
||||||
|
PyErr_SetString(PyExc_TypeError, "store_in_mipmap expects a plMipmap, sequence of Buffer and int"); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (compression == plBitmap::kDirectXCompression) |
||||||
|
mipmap->CompressImage(i, item->m_buffer, item->m_size); |
||||||
|
else |
||||||
|
mipmap->setLevelData(i, item->m_buffer, item->m_size); |
||||||
|
} |
||||||
|
|
||||||
|
Py_RETURN_NONE; |
||||||
|
} |
||||||
|
|
||||||
|
static PyMethodDef pyGLTexture_Methods[] = { |
||||||
|
{ _pycs("__enter__"), (PyCFunction)pyGLTexture__enter__, METH_NOARGS, NULL }, |
||||||
|
{ _pycs("__exit__"), (PyCFunction)pyGLTexture__enter__, METH_VARARGS, NULL }, |
||||||
|
|
||||||
|
{ _pycs("generate_mipmap"), (PyCFunction)pyGLTexture_generate_mipmap, METH_NOARGS, NULL }, |
||||||
|
{ _pycs("get_level_data"), (PyCFunction)pyGLTexture_get_level_data, METH_KEYWORDS | METH_VARARGS, NULL }, |
||||||
|
{ _pycs("store_in_mipmap"), (PyCFunction)pyGLTexture_store_in_mipmap, METH_VARARGS, NULL }, |
||||||
|
{ NULL, NULL, 0, NULL } |
||||||
|
}; |
||||||
|
|
||||||
|
static PyObject* pyGLTexture_get_has_alpha(pyGLTexture* self, void*) { |
||||||
|
size_t bufsz; |
||||||
|
uint8_t* buf = _get_level_data(self, 0, false, &bufsz); |
||||||
|
for (size_t i = 3; i < bufsz; i += 4) { |
||||||
|
if (buf[i] != 255) { |
||||||
|
delete[] buf; |
||||||
|
return PyBool_FromLong(1); |
||||||
|
} |
||||||
|
} |
||||||
|
delete[] buf; |
||||||
|
return PyBool_FromLong(0); |
||||||
|
} |
||||||
|
|
||||||
|
static PyGetSetDef pyGLTexture_GetSet[] = { |
||||||
|
{ _pycs("has_alpha"), (getter)pyGLTexture_get_has_alpha, NULL, NULL, NULL }, |
||||||
|
{ NULL, NULL, NULL, NULL, NULL } |
||||||
|
}; |
||||||
|
|
||||||
|
PyTypeObject pyGLTexture_Type = { |
||||||
|
PyVarObject_HEAD_INIT(NULL, 0) |
||||||
|
"_korlib.GLTexture", /* tp_name */ |
||||||
|
sizeof(pyGLTexture), /* tp_basicsize */ |
||||||
|
0, /* tp_itemsize */ |
||||||
|
|
||||||
|
(destructor)pyGLTexture_dealloc, /* tp_dealloc */ |
||||||
|
NULL, /* tp_print */ |
||||||
|
NULL, /* tp_getattr */ |
||||||
|
NULL, /* tp_setattr */ |
||||||
|
NULL, /* tp_compare */ |
||||||
|
NULL, /* tp_repr */ |
||||||
|
NULL, /* tp_as_number */ |
||||||
|
NULL, /* tp_as_sequence */ |
||||||
|
NULL, /* tp_as_mapping */ |
||||||
|
NULL, /* tp_hash */ |
||||||
|
NULL, /* tp_call */ |
||||||
|
NULL, /* tp_str */ |
||||||
|
NULL, /* tp_getattro */ |
||||||
|
NULL, /* tp_setattro */ |
||||||
|
NULL, /* tp_as_buffer */ |
||||||
|
|
||||||
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ |
||||||
|
"GLTexture", /* tp_doc */ |
||||||
|
|
||||||
|
NULL, /* tp_traverse */ |
||||||
|
NULL, /* tp_clear */ |
||||||
|
NULL, /* tp_richcompare */ |
||||||
|
0, /* tp_weaklistoffset */ |
||||||
|
NULL, /* tp_iter */ |
||||||
|
NULL, /* tp_iternext */ |
||||||
|
|
||||||
|
pyGLTexture_Methods, /* tp_methods */ |
||||||
|
NULL, /* tp_members */ |
||||||
|
pyGLTexture_GetSet, /* tp_getset */ |
||||||
|
NULL, /* tp_base */ |
||||||
|
NULL, /* tp_dict */ |
||||||
|
NULL, /* tp_descr_get */ |
||||||
|
NULL, /* tp_descr_set */ |
||||||
|
0, /* tp_dictoffset */ |
||||||
|
|
||||||
|
(initproc)pyGLTexture___init__, /* tp_init */ |
||||||
|
NULL, /* tp_alloc */ |
||||||
|
pyGLTexture_new, /* tp_new */ |
||||||
|
NULL, /* tp_free */ |
||||||
|
NULL, /* tp_is_gc */ |
||||||
|
|
||||||
|
NULL, /* tp_bases */ |
||||||
|
NULL, /* tp_mro */ |
||||||
|
NULL, /* tp_cache */ |
||||||
|
NULL, /* tp_subclasses */ |
||||||
|
NULL, /* tp_weaklist */ |
||||||
|
|
||||||
|
NULL, /* tp_del */ |
||||||
|
0, /* tp_version_tag */ |
||||||
|
NULL, /* tp_finalize */ |
||||||
|
}; |
||||||
|
|
||||||
|
PyObject* Init_pyGLTexture_Type() { |
||||||
|
if (PyType_Ready(&pyGLTexture_Type) < 0) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
Py_INCREF(&pyGLTexture_Type); |
||||||
|
return (PyObject*)&pyGLTexture_Type; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
||||||
|
|
@ -0,0 +1,29 @@ |
|||||||
|
/* This file is part of Korman.
|
||||||
|
* |
||||||
|
* Korman 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 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _KORLIB_TEXTURE_H |
||||||
|
#define _KORLIB_TEXTURE_H |
||||||
|
|
||||||
|
#include "korlib.h" |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
|
||||||
|
extern PyTypeObject pyGLTexture_Type; |
||||||
|
PyObject* Init_pyGLTexture_Type(); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _KORLIB_TEXTURE_H
|
@ -0,0 +1,19 @@ |
|||||||
|
# This file is part of Korman. |
||||||
|
# |
||||||
|
# Korman 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 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
try: |
||||||
|
from _korlib import * |
||||||
|
except ImportError: |
||||||
|
from .texture import * |
@ -0,0 +1,109 @@ |
|||||||
|
# This file is part of Korman. |
||||||
|
# |
||||||
|
# Korman 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 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# Korman 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 Korman. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bgl |
||||||
|
from PyHSPlasma import plBitmap |
||||||
|
|
||||||
|
# BGL doesn't know about this as of Blender 2.74 |
||||||
|
bgl.GL_GENERATE_MIPMAP = 0x8191 |
||||||
|
bgl.GL_BGRA = 0x80E1 |
||||||
|
|
||||||
|
class GLTexture: |
||||||
|
def __init__(self, blimg): |
||||||
|
self._ownit = (blimg.bindcode == 0) |
||||||
|
self._blimg = blimg |
||||||
|
|
||||||
|
def __enter__(self): |
||||||
|
"""Sets the Blender Image as the active OpenGL texture""" |
||||||
|
if self._ownit: |
||||||
|
if self._blimg.gl_load() != 0: |
||||||
|
raise RuntimeError("failed to load image") |
||||||
|
|
||||||
|
self._previous_texture = self._get_integer(bgl.GL_TEXTURE_BINDING_2D) |
||||||
|
self._changed_state = (self._previous_texture != self._blimg.bindcode) |
||||||
|
if self._changed_state: |
||||||
|
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self._blimg.bindcode) |
||||||
|
return self |
||||||
|
|
||||||
|
def __exit__(self, type, value, traceback): |
||||||
|
mipmap_state = getattr(self, "_mipmap_state", None) |
||||||
|
if mipmap_state is not None: |
||||||
|
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_GENERATE_MIPMAP, mipmap_state) |
||||||
|
if self._changed_state: |
||||||
|
bgl.glBindTexture(bgl.GL_TEXTURE_2D, self._previous_texture) |
||||||
|
if self._ownit: |
||||||
|
self._blimg.gl_free() |
||||||
|
|
||||||
|
def generate_mipmap(self): |
||||||
|
"""Generates all mip levels for this texture""" |
||||||
|
self._mipmap_state = self._get_tex_param(bgl.GL_GENERATE_MIPMAP) |
||||||
|
|
||||||
|
# Note that this is a very old feature from OpenGL 1.x -- it's new enough that Windows (and |
||||||
|
# Blender apparently) don't support it natively and yet old enough that it was thrown away |
||||||
|
# in OpenGL 3.0. The new way is glGenerateMipmap, but Blender likes oldgl, so we don't have that |
||||||
|
# function available to us in BGL. I don't want to deal with loading the GL dll in ctypes on |
||||||
|
# many platforms right now (or context headaches). If someone wants to fix this, be my guest! |
||||||
|
# It will simplify our state tracking a bit. |
||||||
|
bgl.glTexParameteri(bgl.GL_TEXTURE_2D, bgl.GL_GENERATE_MIPMAP, 1) |
||||||
|
|
||||||
|
def get_level_data(self, level=0, calc_alpha=False, bgra=False, quiet=False): |
||||||
|
"""Gets the uncompressed pixel data for a requested mip level, optionally calculating the alpha |
||||||
|
channel from the image color data |
||||||
|
""" |
||||||
|
width = self._get_tex_param(bgl.GL_TEXTURE_WIDTH, level) |
||||||
|
height = self._get_tex_param(bgl.GL_TEXTURE_HEIGHT, level) |
||||||
|
if not quiet: |
||||||
|
print(" Level #{}: {}x{}".format(level, width, height)) |
||||||
|
|
||||||
|
# Grab the image data |
||||||
|
size = width * height * 4 |
||||||
|
buf = bgl.Buffer(bgl.GL_BYTE, size) |
||||||
|
fmt = bgl.GL_BGRA if bgra else bgl.GL_RGBA |
||||||
|
bgl.glGetTexImage(bgl.GL_TEXTURE_2D, level, fmt, bgl.GL_UNSIGNED_BYTE, buf); |
||||||
|
|
||||||
|
# Calculate le alphas |
||||||
|
# NOTE: the variable names are correct for GL_RGBA. We'll still get the right values for |
||||||
|
# BGRA, obviously, but red will suddenly be... blue. Yeah. |
||||||
|
if calc_alpha: |
||||||
|
for i in range(0, size, 4): |
||||||
|
r, g, b = buf[i:i+3] |
||||||
|
buf[i+3] = int((r + g + b) / 3) |
||||||
|
return bytes(buf) |
||||||
|
|
||||||
|
def _get_integer(self, arg): |
||||||
|
buf = bgl.Buffer(bgl.GL_INT, 1) |
||||||
|
bgl.glGetIntegerv(arg, buf) |
||||||
|
return int(buf[0]) |
||||||
|
|
||||||
|
def _get_tex_param(self, param, level=None): |
||||||
|
buf = bgl.Buffer(bgl.GL_INT, 1) |
||||||
|
if level is None: |
||||||
|
bgl.glGetTexParameteriv(bgl.GL_TEXTURE_2D, param, buf) |
||||||
|
else: |
||||||
|
bgl.glGetTexLevelParameteriv(bgl.GL_TEXTURE_2D, level, param, buf) |
||||||
|
return int(buf[0]) |
||||||
|
|
||||||
|
@property |
||||||
|
def has_alpha(self): |
||||||
|
data = self.get_level_data(quiet=True) |
||||||
|
for i in range(3, len(data), 4): |
||||||
|
if data[i] != 255: |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
def store_in_mipmap(self, mipmap, data, compression): |
||||||
|
func = mipmap.CompressImage if compression == plBitmap.kDirectXCompression else mipmap.setLevel |
||||||
|
for i, level in enumerate(data): |
||||||
|
func(i, level) |
Loading…
Reference in new issue