From d48fd9527f78f60c0616be07bdc08474ee7b50fc Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 16 Jul 2015 20:01:56 -0400 Subject: [PATCH] Begin moving slow things into korlib Korlib will have two components: a python implementation in Korman and a C++ "_korlib" module that will be insanely fast. Users will apreciate it, and devs will be glad they don't have to compile the C version. --- korman/exporter/material.py | 88 ++------------------------------- korman/korlib/__init__.py | 19 ++++++++ korman/korlib/texture.py | 97 +++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 85 deletions(-) create mode 100644 korman/korlib/__init__.py create mode 100644 korman/korlib/texture.py diff --git a/korman/exporter/material.py b/korman/exporter/material.py index 67ddb10..41148b9 100644 --- a/korman/exporter/material.py +++ b/korman/exporter/material.py @@ -14,7 +14,6 @@ # along with Korman. If not, see . import bpy -import bgl import math import os.path from PyHSPlasma import * @@ -22,90 +21,9 @@ import weakref from . import explosions from .. import helpers +from .. import korlib 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: def __init__(self, texture=None, image=None, use_alpha=None, force_calc_alpha=False): assert (texture or image) @@ -570,7 +488,7 @@ class MaterialConverter: numLevels = max(numLevels - 2, 2) # Grab the image data from OpenGL and stuff it into the plBitmap - with _GLTexture(image) as glimage: + with korlib.GLTexture(image) as glimage: if key.mipmap: print(" Generating mip levels") glimage.generate_mipmap() @@ -658,7 +576,7 @@ class MaterialConverter: result = False else: # 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) for i in range(3, len(data), 4): if data[i] != 255: diff --git a/korman/korlib/__init__.py b/korman/korlib/__init__.py new file mode 100644 index 0000000..a2c2644 --- /dev/null +++ b/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 . + +try: + from _korlib import * +except ImportError: + from .texture import * diff --git a/korman/korlib/texture.py b/korman/korlib/texture.py new file mode 100644 index 0000000..93d00ec --- /dev/null +++ b/korman/korlib/texture.py @@ -0,0 +1,97 @@ +# 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 . + +import bgl + +# 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])