Browse Source

Merge remote-tracking branch 'hoikas/korlib'

pull/11/head
Adam Johnson 9 years ago
parent
commit
1b726330e2
  1. 3
      .gitignore
  2. 2
      installer/Installer.nsi
  3. 38
      korlib/CMakeLists.txt
  4. 111
      korlib/buffer.cpp
  5. 37
      korlib/buffer.h
  6. 35
      korlib/korlib.h
  7. 45
      korlib/module.cpp
  8. 289
      korlib/texture.cpp
  9. 29
      korlib/texture.h
  10. 5
      korman/exporter/explosions.py
  11. 101
      korman/exporter/material.py
  12. 19
      korman/korlib/__init__.py
  13. 109
      korman/korlib/texture.py

3
.gitignore vendored

@ -35,3 +35,6 @@ pip-log.txt
# Installer Stuff # Installer Stuff
installer/Files installer/Files
installer/korman.exe installer/korman.exe
# C++ korlib build directory
korlib/build

2
installer/Installer.nsi

@ -153,6 +153,7 @@ Section "Files"
File "Files\HSPlasma.dll" File "Files\HSPlasma.dll"
File "Files\PyHSPlasma.pyd" File "Files\PyHSPlasma.pyd"
File "Files\NxCooking.dll" File "Files\NxCooking.dll"
File "Files\_korlib.pyd"
WriteRegStr HKLM "Software\Korman" "" $INSTDIR WriteRegStr HKLM "Software\Korman" "" $INSTDIR
WriteUninstaller "$INSTDIR\korman_uninstall.exe" WriteUninstaller "$INSTDIR\korman_uninstall.exe"
@ -164,5 +165,6 @@ Section "Uninstall"
Delete "$INSTDIR\python\lib\site-packages\HSPlasma.dll" Delete "$INSTDIR\python\lib\site-packages\HSPlasma.dll"
Delete "$INSTDIR\python\lib\site-packages\PyHSPlasma.pyd" Delete "$INSTDIR\python\lib\site-packages\PyHSPlasma.pyd"
Delete "$INSTDIR\python\lib\site-packages\NxCooking.dll" Delete "$INSTDIR\python\lib\site-packages\NxCooking.dll"
Delete "$INSTDIR\python\lib\site-packages\_korlib.pyd"
DeleteRegKey /ifempty HKLM "Software\Korman" DeleteRegKey /ifempty HKLM "Software\Korman"
SectionEnd SectionEnd

38
korlib/CMakeLists.txt

@ -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})

111
korlib/buffer.cpp

@ -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;
}
};

37
korlib/buffer.h

@ -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

35
korlib/korlib.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

45
korlib/module.cpp

@ -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;
}
};

289
korlib/texture.cpp

@ -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;
}
};

29
korlib/texture.h

@ -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

5
korman/exporter/explosions.py

@ -23,11 +23,6 @@ class BlenderOptionNotSupportedError(ExportError):
super(ExportError, self).__init__("Unsupported Blender Option: '{}'".format(opt)) super(ExportError, self).__init__("Unsupported Blender Option: '{}'".format(opt))
class GLLoadError(ExportError):
def __init__(self, image):
super(ExportError, self).__init__("Failed to load '{}' into OpenGL".format(image.name))
class TooManyUVChannelsError(ExportError): class TooManyUVChannelsError(ExportError):
def __init__(self, obj, mat): def __init__(self, obj, mat):
msg = "There are too many UV Textures on the material '{}' associated with object '{}'.".format( msg = "There are too many UV Textures on the material '{}' associated with object '{}'.".format(

101
korman/exporter/material.py

@ -14,7 +14,6 @@
# along with Korman. If not, see <http://www.gnu.org/licenses/>. # along with Korman. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import bgl
import math import math
import os.path import os.path
from PyHSPlasma import * from PyHSPlasma import *
@ -22,90 +21,9 @@ import weakref
from . import explosions from . import explosions
from .. import helpers from .. import helpers
from .. import korlib
from . import utils from . import utils
# 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)
if self._ownit:
if blimg.gl_load() != 0:
raise explosions.GLLoadError(blimg)
self._blimg = blimg
def __del__(self):
if self._ownit:
self._blimg.gl_free()
def __enter__(self):
"""Sets the Blender Image as the active OpenGL texture"""
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)
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])
class _Texture: class _Texture:
def __init__(self, texture=None, image=None, use_alpha=None, force_calc_alpha=False): def __init__(self, texture=None, image=None, use_alpha=None, force_calc_alpha=False):
assert (texture or image) assert (texture or image)
@ -570,7 +488,8 @@ class MaterialConverter:
numLevels = max(numLevels - 2, 2) numLevels = max(numLevels - 2, 2)
# Grab the image data from OpenGL and stuff it into the plBitmap # Grab the image data from OpenGL and stuff it into the plBitmap
with _GLTexture(image) as glimage: helper = korlib.GLTexture(image)
with helper as glimage:
if key.mipmap: if key.mipmap:
print(" Generating mip levels") print(" Generating mip levels")
glimage.generate_mipmap() glimage.generate_mipmap()
@ -605,9 +524,7 @@ class MaterialConverter:
if page not in pages: if page not in pages:
mipmap = plMipmap(name=name, width=eWidth, height=eHeight, numLevels=numLevels, mipmap = plMipmap(name=name, width=eWidth, height=eHeight, numLevels=numLevels,
compType=compression, format=plBitmap.kRGB8888, dxtLevel=dxt) compType=compression, format=plBitmap.kRGB8888, dxtLevel=dxt)
func = mipmap.CompressImage if compression == plBitmap.kDirectXCompression else mipmap.setLevel helper.store_in_mipmap(mipmap, data, compression)
for i, level in enumerate(data):
func(i, level)
mgr.AddObject(page, mipmap) mgr.AddObject(page, mipmap)
pages[page] = mipmap pages[page] = mipmap
else: else:
@ -658,14 +575,8 @@ class MaterialConverter:
result = False result = False
else: else:
# Using bpy.types.Image.pixels is VERY VERY VERY slow... # Using bpy.types.Image.pixels is VERY VERY VERY slow...
with _GLTexture(image) as glimage: with korlib.GLTexture(image) as glimage:
data = glimage.get_level_data(quiet=True) result = glimage.has_alpha
for i in range(3, len(data), 4):
if data[i] != 255:
result = True
break
else:
result = False
self._alphatest[image] = result self._alphatest[image] = result
return result return result

19
korman/korlib/__init__.py

@ -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 *

109
korman/korlib/texture.py

@ -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…
Cancel
Save