Exemplo n.º 1
0
 def read_zbuffer(xy, wh=(1, 1), centered=False, src=None):
     if isinstance(wh, (int, float)):
         wh = (wh, wh)
     elif len(wh) < 2:
         wh = (wh[0], wh[0])
     
     x, y, w, h = int(xy[0]), int(xy[1]), int(wh[0]), int(wh[1])
     
     if centered:
         x -= w // 2
         y -= h // 2
     
     buf_size = w*h
     
     if src is None:
         # xy is in window coordinates!
         zbuf = Buffer(GL_FLOAT, [buf_size])
         glReadPixels(x, y, w, h, GL_DEPTH_COMPONENT, GL_FLOAT, zbuf)
     else:
         src, w0, h0 = src
         template = [0.0] * buf_size
         for dy in range(h):
             y0 = min(max(y + dy, 0), h0-1)
             for dx in range(w):
                 x0 = min(max(x + dx, 0), w0-1)
                 i0 = x0 + y0 * w0
                 i1 = dx + dy * w
                 template[i1] = src[i0]
         zbuf = Buffer(GL_FLOAT, [buf_size], template)
     
     return zbuf
Exemplo n.º 2
0
	def glGenTextures(n, textures=None):
		id_buf = Buffer(GL_INT, n)
		_glGenTextures(n, id_buf)

		if textures:
			textures.extend(id_buf.to_list())

		return id_buf.to_list()[0] if n == 1 else id_buf.to_list()
Exemplo n.º 3
0
    def __init__(self,
                 toolkit: BlenderToolkit,
                 look_and_feel: Optional[LookAndFeel] = None,
                 font_options: Optional[FontOptions] = None,
                 window_manager: Optional[WindowManager] = None,
                 error_handler: Optional[ErrorHandler] = None) -> None:
        super().__init__(toolkit, look_and_feel, font_options, window_manager,
                         error_handler)

        resolution = Dimension(bge.render.getWindowWidth(),
                               bge.render.getWindowHeight())

        self._resolution = BehaviorSubject(resolution)

        # noinspection PyTypeChecker
        self.window_size = self._resolution.pipe(ops.distinct_until_changed())

        self._shader = cast(GPUShader, gpu.shader.from_builtin("2D_IMAGE"))

        if use_viewport_render:
            # noinspection PyArgumentList
            self._draw_handler = SpaceView3D.draw_handler_add(
                self.process, (), "WINDOW", "POST_PIXEL")
        else:
            self._draw_handler = None

            bge.logic.getCurrentScene().post_draw.append(self.process)

        # noinspection PyTypeChecker
        self._texture = Buffer(bgl.GL_INT, 1)

        bgl.glGenTextures(1, self.texture)
Exemplo n.º 4
0
def _create_buffer(key: int, buffer_type: int, dimensions: Union[int, List[int]], template=None) -> NoReturn:
    if template is None:
        _buffer_instances[key] = Buffer(buffer_type, dimensions)
    else:
        _buffer_instances[key] = Buffer(buffer_type, dimensions, template)
    _buffer_types[key] = buffer_type
Exemplo n.º 5
0
	def glGetIntegerv(pname):
		# Only used for GL_VIEWPORT right now, so assume we want a size 4 Buffer
		buf = Buffer(GL_INT, 4)
		_glGetIntegerv(pname, buf)
		return buf.to_list()
Exemplo n.º 6
0
 def matrix_to_buffer(matrix, dtype=GL_FLOAT):
     return Buffer(dtype, 16, [matrix[i][j] for i in range4 for j in range4])
Exemplo n.º 7
0
def initialize():
    import math
    from collections import namedtuple
    
    from mathutils import Color, Vector, Matrix, Quaternion, Euler
    
    import bgl
    import blf
    import gpu
    
    from bgl import (
        Buffer,
        GL_TRUE, GL_FALSE, glIsEnabled, glEnable, glDisable,
        GL_INT, glGetIntegerv,
        GL_FLOAT, glGetFloatv,
        GL_DEPTH_COMPONENT, glReadPixels,
    )
    
    from gpu.types import GPUBatch, GPUIndexBuf, GPUOffScreen, GPUShader, GPUVertBuf, GPUVertFormat
    
    shader_from_builtin = gpu.shader.from_builtin
    
    blf_options = {'ROTATION':blf.ROTATION, 'CLIPPING':blf.CLIPPING, 'SHADOW':blf.SHADOW,
        'KERNING_DEFAULT':blf.KERNING_DEFAULT, 'WORD_WRAP':blf.WORD_WRAP, 'MONOCHROME':blf.MONOCHROME}
    
    blf_load = blf.load
    blf_unload = blf.unload
    blf_enable = blf.enable
    blf_disable = blf.disable
    blf_shadow = blf.shadow
    blf_shadow_offset = blf.shadow_offset
    blf_color = blf.color
    blf_position = blf.position
    blf_rotation = blf.rotation
    blf_size = blf.size
    blf_clipping = blf.clipping
    blf_aspect = blf.aspect
    blf_dimensions = blf.dimensions
    blf_draw = blf.draw
    blf_word_wrap = blf.word_wrap
    
    class StateRestorator:
        __slots__ = ("state", "target")
        
        def __init__(self, target, args, kwargs):
            state = {}
            for k in args:
                state[k] = getattr(target, k)
            for k, v in kwargs.items():
                state[k] = getattr(target, k)
                setattr(target, k, v)
            self.state = state
            self.target = target
        
        def restore(self):
            target = self.target
            for k, v in self.state.items():
                setattr(target, k, v)
        
        def __enter__(self):
            return self
        
        def __exit__(self, type, value, traceback):
            self.restore()
    
    class CGL:
        def __call__(self, *args, **kwargs):
            return StateRestorator(self, args, kwargs)
        
        # Adapted from gpu_extras.batch.batch_for_shader()
        @staticmethod
        def batch(*args, **attr_data):
            """
            Positional arguments: vbo format | shader | built-in shader name, primitive type, [indices]
            Keyword arguments: data for the corresponding shader attributes
            """
            
            arg_count = len(args)
            if (arg_count < 2) or (arg_count > 3):
                raise TypeError(f"batch() takes from 2 to 3 positional arguments but {arg_count} were given")
            
            vbo_len = 0
            for data in attr_data.values():
                if not data: continue
                data_len = len(data)
                if data_len == vbo_len: continue
                if vbo_len == 0:
                    vbo_len = data_len
                else:
                    raise ValueError("Length mismatch for vertex attribute data")
            
            vbo_format = args[0]
            if isinstance(vbo_format, str): vbo_format = shader_from_builtin(vbo_format)
            if isinstance(vbo_format, GPUShader): vbo_format = vbo_format.format_calc()
            
            primitive_type = args[1]
            
            indices = (args[2] if arg_count > 2 else None)
            
            vbo = GPUVertBuf(vbo_format, vbo_len)
            for id, data in attr_data.items():
                vbo.attr_fill(id, data)
            
            if indices is None:
                return GPUBatch(type=primitive_type, buf=vbo)
            else:
                ibo = GPUIndexBuf(type=primitive_type, seq=indices)
                return GPUBatch(type=primitive_type, buf=vbo, elem=ibo)
        
        @staticmethod
        def read_zbuffer(xy, wh=(1, 1), centered=False, src=None):
            if isinstance(wh, (int, float)):
                wh = (wh, wh)
            elif len(wh) < 2:
                wh = (wh[0], wh[0])
            
            x, y, w, h = int(xy[0]), int(xy[1]), int(wh[0]), int(wh[1])
            
            if centered:
                x -= w // 2
                y -= h // 2
            
            buf_size = w*h
            
            if src is None:
                # xy is in window coordinates!
                zbuf = Buffer(GL_FLOAT, [buf_size])
                glReadPixels(x, y, w, h, GL_DEPTH_COMPONENT, GL_FLOAT, zbuf)
            else:
                src, w0, h0 = src
                template = [0.0] * buf_size
                for dy in range(h):
                    y0 = min(max(y + dy, 0), h0-1)
                    for dx in range(w):
                        x0 = min(max(x + dx, 0), w0-1)
                        i0 = x0 + y0 * w0
                        i1 = dx + dy * w
                        template[i1] = src[i0]
                zbuf = Buffer(GL_FLOAT, [buf_size], template)
            
            return zbuf

    cgl = CGL()
    
    # ========== TEXT ========== #
    
    class TextWrapper:
        # dimensions & wrapping calculation
        @classmethod
        def dimensions(cls, text, font=0):
            return blf_dimensions(font, text)
        
        @classmethod
        def _split_word(cls, width, x, max_x, word, lines, font):
            line = ""
            
            for c in word:
                x_dx = x + blf_dimensions(font, line+c)[0]
                
                if (x_dx) > width:
                    x_dx = blf_dimensions(font, line)[0]
                    lines.append(line)
                    line = c
                    x = 0
                else:
                    line += c
                
                max_x = max(x_dx, max_x)
            
            return line, x, max_x

        @classmethod
        def _split_line(cls, width, x, max_x, line, lines, font):
            words = line.split(" ")
            line = ""
            
            for word in words:
                c = (word if not line else " " + word)
                x_dx = x + blf_dimensions(font, line+c)[0]
                
                if (x_dx) > width:
                    x_dx = blf_dimensions(font, line)[0]
                    if not line:
                        # one word is longer than the width
                        line, x, max_x = cls._split_word(width, x, max_x, word, lines, font)
                    else:
                        lines.append(line)
                        line = word
                    x = 0
                else:
                    line += c
                
                max_x = max(x_dx, max_x)
            
            if line: lines.append(line)
            
            return max_x

        @classmethod
        def split_text(cls, width, x, max_x, text, lines, font=0):
            for line in text.splitlines():
                if not line:
                    lines.append("")
                else:
                    max_x = cls._split_line(width, x, max_x, line, lines, font)
                x = 0
            
            return max_x

        @classmethod
        def wrap_text(cls, text, width, indent=0, font=0):
            """
            Splits text into lines that don't exceed the given width.
            text -- the text.
            width -- the width the text should fit into.
            font -- the id of the typeface as returned by blf.load(). Defaults to 0 (the default font).
            indent -- the indent of the paragraphs. Defaults to 0.
            Returns: lines, actual_width
            lines -- the list of the resulting lines
            actual_width -- the max width of these lines (may be less than the supplied width).
            """
            
            line_height = blf_dimensions(font, "!")[1]
            
            lines = []
            max_x = 0
            for line in text.splitlines():
                if not line:
                    lines.append("")
                else:
                    max_x = cls._split_line(width, indent, max_x, line, lines, font)
            
            return lines, (max_x, len(lines)*line_height)
    
    class BatchedText:
        __slots__ = ("font", "pieces", "size")
        
        def __init__(self, font, pieces, size):
            self.font = font
            self.pieces = pieces
            self.size = size
        
        # WARNING: modifies BLEND state
        def draw(self, pos, origin=None):
            x = pos[0]
            y = pos[1]
            z = (pos[2] if len(pos) > 2 else 0)
            
            if origin:
                x -= self.size[0] * origin[0]
                y -= self.size[1] * origin[1]
            
            cgl.BLEND = True
            
            font = self.font
            x0, y0 = round(x), round(y)
            for txt, x, y in self.pieces:
                blf_position(font, x0+x, y0+y, z)
                blf_draw(font, txt)
    
    class Text:
        font = 0 # 0 is the default font
        
        # load / unload
        def load(self, filename, size=None, dpi=72):
            font = blf_load(filename)
            if size is not None: blf_size(font, size, dpi)
            return font
        def unload(self, filename):
            blf_unload(filename)
        
        # enable / disable options
        def enable(self, option):
            blf_enable(self.font, blf_options[option])
        def disable(self, option):
            blf_disable(self.font, blf_options[option])
        
        # set effects (shadow, blur)
        def shadow(self, level, r, g, b, a):
            blf_shadow(self.font, level, r, g, b, a)
        def shadow_offset(self, x, y):
            blf_shadow_offset(self.font, x, y)
        def blur(self, radius):
            blf_blur(self.font, radius)
        
        # set position / rotation / size
        def position(self, x, y, z=0.0):
            blf_position(self.font, x, y, z)
        def rotation(self, angle):
            blf_rotation(self.font, angle)
        def size(self, size, dpi=72):
            blf_size(self.font, size, dpi)
        
        # set clipping / aspect
        def clipping(self, xmin, ymin, xmax, ymax):
            blf_clipping(self.font, xmin, ymin, xmax, ymax)
        def aspect(self, aspect):
            blf_aspect(self.font, aspect)
        
        def compile(self, text, width=None, alignment=None):
            font = self.font
            
            if width is None:
                lines, size = [text], blf_dimensions(font, text)
            else:
                lines, size = TextWrapper.wrap_text(text, width, font=font)
            
            if (alignment in (None, 'LEFT')): alignment = 0.0
            elif (alignment == 'CENTER'): alignment = 0.5
            elif (alignment == 'RIGHT'): alignment = 1.0
            
            pieces = []
            x, y = 0, 0
            w, h = size
            for line in lines:
                line_size = blf_dimensions(font, line)
                x = (w - line_size[0]) * alignment
                pieces.append((line, round(x), round(y)))
                y += line_size[1]
            
            return BatchedText(font, pieces, size)
        
        def draw(self, text, pos=None, origin=None, width=None, alignment=None):
            font = self.font
            
            if pos is None: # if position not specified, other calculations cannot be performed
                blf_draw(font, text)
            elif width is not None: # wrap+align
                lines, size = TextWrapper.wrap_text(text, width, font=font)
                
                x = pos[0]
                y = pos[1]
                z = (pos[2] if len(pos) > 2 else 0)
                
                if origin:
                    x -= size[0] * origin[0]
                    y -= size[1] * origin[1]
                
                if (alignment in (None, 'LEFT')): alignment = 0.0
                elif (alignment == 'CENTER'): alignment = 0.5
                elif (alignment == 'RIGHT'): alignment = 1.0
                
                x0, y0 = x, y
                w, h = size
                for line in lines:
                    line_size = blf_dimensions(font, line)
                    x = x0 + (w - line_size[0]) * alignment
                    blf_position(font, round(x), round(y), z)
                    blf_draw(font, line)
                    y += line_size[1]
            else:
                x = pos[0]
                y = pos[1]
                z = (pos[2] if len(pos) > 2 else 0)
                
                if origin:
                    size = blf_dimensions(font, text)
                    x -= size[0] * origin[0]
                    y -= size[1] * origin[1]
                
                blf_position(font, round(x), round(y), z)
                blf_draw(font, text)
    
    cgl.text = Text()
    
    # ========== GL API ========== #
    
    def Cap(name, doc=""):
        pname = name[3:]
        if hasattr(CGL, pname): return
        
        state_id = getattr(bgl, name, None)
        if state_id is None: return
        
        class Descriptor:
            __doc__ = doc
            def __get__(self, instance, owner):
                return glIsEnabled(state_id)
            def __set__(self, instance, value):
                (glEnable if value else glDisable)(state_id)
        
        setattr(CGL, pname, Descriptor())
    
    def add_descriptor(name, getter, setter, doc=""):
        #Descriptor = type(name+"_Descriptor", (), {"__doc__":doc, "__get__":getter, "__set__":setter})
        class Descriptor:
            __doc__ = doc
            __get__ = getter
            __set__ = setter
        setattr(CGL, name, Descriptor())
    
    def map_enum(*names):
        enum_k2v = {}
        enum_v2k = {}
        for name in names:
            if name.startswith("GL_"): name = name[3:]
            value = getattr(bgl, "GL_"+name, None)
            if value is None: continue # bgl does not always expose everything
            enum_k2v[name] = value
            enum_v2k[value] = name
        return enum_k2v, enum_v2k
    
    # OpenGL 4-4.5 level capabilities
    # https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnable.xhtml
    Cap('GL_BLEND')
    Cap('GL_CLIP_DISTANCE0')
    Cap('GL_CLIP_DISTANCE1')
    Cap('GL_CLIP_DISTANCE2')
    Cap('GL_CLIP_DISTANCE3')
    Cap('GL_CLIP_DISTANCE4')
    Cap('GL_CLIP_DISTANCE5')
    Cap('GL_COLOR_LOGIC_OP')
    Cap('GL_CULL_FACE')
    Cap('GL_DEBUG_OUTPUT')
    Cap('GL_DEBUG_OUTPUT_SYNCHRONOUS')
    Cap('GL_DEPTH_CLAMP')
    Cap('GL_DEPTH_TEST')
    Cap('GL_DITHER')
    Cap('GL_FRAMEBUFFER_SRGB')
    Cap('GL_LINE_SMOOTH')
    Cap('GL_MULTISAMPLE')
    Cap('GL_POLYGON_OFFSET_FILL')
    Cap('GL_POLYGON_OFFSET_LINE')
    Cap('GL_POLYGON_OFFSET_POINT')
    Cap('GL_POLYGON_SMOOTH')
    Cap('GL_PRIMITIVE_RESTART')
    Cap('GL_PRIMITIVE_RESTART_FIXED_INDEX')
    Cap('GL_RASTERIZER_DISCARD')
    Cap('GL_SAMPLE_ALPHA_TO_COVERAGE')
    Cap('GL_SAMPLE_ALPHA_TO_ONE')
    Cap('GL_SAMPLE_COVERAGE')
    Cap('GL_SAMPLE_SHADING')
    Cap('GL_SAMPLE_MASK')
    Cap('GL_SCISSOR_TEST')
    Cap('GL_STENCIL_TEST')
    Cap('GL_TEXTURE_CUBE_MAP_SEAMLESS')
    Cap('GL_PROGRAM_POINT_SIZE') # old name: GL_VERTEX_PROGRAM_POINT_SIZE
    
    range4 = tuple(range(4))
    def matrix_to_buffer(matrix, dtype=GL_FLOAT):
        return Buffer(dtype, 16, [matrix[i][j] for i in range4 for j in range4])
    def buffer_to_matrix(buf):
        return Matrix((buf[0:4], buf[4:8], buf[8:12], buf[12:16]))
    
    cgl.matrix_to_buffer = staticmethod(matrix_to_buffer)
    cgl.buffer_to_matrix = staticmethod(buffer_to_matrix)
    
    int1buf0 = Buffer(GL_INT, 1)
    int1buf1 = Buffer(GL_INT, 1)
    int1buf2 = Buffer(GL_INT, 1)
    int1buf3 = Buffer(GL_INT, 1)
    float1buf0 = Buffer(GL_FLOAT, 1)
    float1buf1 = Buffer(GL_FLOAT, 1)
    float2buf0 = Buffer(GL_FLOAT, 2)
    matrixbuf0 = Buffer(GL_FLOAT, 16)
    
    if hasattr(bgl, "glLineWidth"):
        from bgl import GL_LINE_WIDTH, glLineWidth
        def _get(self, instance, owner):
            glGetFloatv(GL_LINE_WIDTH, float1buf0)
            return float1buf0[0]
        def _set(self, instance, value):
            glLineWidth(value)
        add_descriptor("LineWidth", _get, _set)
    
    blend_funcs_k2v, blend_funcs_v2k = map_enum(
        'GL_ZERO', 'GL_ONE',
        'GL_SRC_COLOR', 'GL_ONE_MINUS_SRC_COLOR',
        'GL_DST_COLOR', 'GL_ONE_MINUS_DST_COLOR',
        'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA',
        'GL_DST_ALPHA', 'GL_ONE_MINUS_DST_ALPHA',
        'GL_CONSTANT_COLOR', 'GL_ONE_MINUS_CONSTANT_COLOR',
        'GL_CONSTANT_ALPHA', 'GL_ONE_MINUS_CONSTANT_ALPHA',
        'GL_SRC_ALPHA_SATURATE',
        'GL_SRC1_COLOR', 'GL_ONE_MINUS_SRC1_COLOR',
        'GL_SRC1_ALPHA', 'GL_ONE_MINUS_SRC1_ALPHA',
    )
    
    if hasattr(bgl, "glBlendFunc"):
        from bgl import GL_BLEND_SRC_RGB, GL_BLEND_DST_RGB, glBlendFunc
        BlendFunc = namedtuple("BlendFunc", ("src", "dst"))
        def _get(self, instance, owner):
            glGetIntegerv(GL_BLEND_SRC_RGB, int1buf0)
            glGetIntegerv(GL_BLEND_DST_RGB, int1buf1)
            return BlendFunc(blend_funcs_v2k[int1buf0[0]], blend_funcs_v2k[int1buf1[0]])
        def _set(self, instance, value):
            glBlendFunc(blend_funcs_k2v[value[0]], blend_funcs_k2v[value[1]])
        add_descriptor("BlendFunc", _get, _set)
    
    if hasattr(bgl, "glBlendFuncSeparate"):
        from bgl import GL_BLEND_SRC_RGB, GL_BLEND_DST_RGB, GL_BLEND_SRC_ALPHA, GL_BLEND_DST_ALPHA, glBlendFuncSeparate
        BlendFuncSeparate = namedtuple("BlendFuncSeparate", ("src_rgb", "dst_rgb", "src_alpha", "dst_alpha"))
        def _get(self, instance, owner):
            glGetIntegerv(GL_BLEND_SRC_RGB, int1buf0)
            glGetIntegerv(GL_BLEND_DST_RGB, int1buf1)
            glGetIntegerv(GL_BLEND_SRC_ALPHA, int1buf2)
            glGetIntegerv(GL_BLEND_DST_ALPHA, int1buf3)
            return BlendFuncSeparate(blend_funcs_v2k[int1buf0[0]], blend_funcs_v2k[int1buf1[0]],
                                     blend_funcs_v2k[int1buf2[0]], blend_funcs_v2k[int1buf3[0]])
        def _set(self, instance, value):
            glBlendFuncSeparate(blend_funcs_k2v[value[0]], blend_funcs_k2v[value[1]],
                                blend_funcs_k2v[value[2]], blend_funcs_k2v[value[3]])
        add_descriptor("BlendFuncSeparate", _get, _set)
    
    depth_funcs_k2v, depth_funcs_v2k = map_enum(
        'GL_NEVER', 'GL_LESS', 'GL_EQUAL', 'GL_LEQUAL', 'GL_GREATER', 'GL_NOTEQUAL', 'GL_GEQUAL', 'GL_ALWAYS'
    )
    
    if hasattr(bgl, "glDepthFunc"):
        from bgl import GL_DEPTH_FUNC, glDepthFunc
        def _get(self, instance, owner):
            glGetIntegerv(GL_DEPTH_FUNC, int1buf0)
            return depth_funcs_v2k[int1buf0[0]]
        def _set(self, instance, value):
            glDepthFunc(depth_funcs_k2v[value])
        add_descriptor("DepthFunc", _get, _set)
    
    if hasattr(bgl, "glDepthMask"):
        from bgl import GL_DEPTH_WRITEMASK, glDepthMask
        def _get(self, instance, owner):
            glGetIntegerv(GL_DEPTH_WRITEMASK, int1buf0)
            return bool(int1buf0[0])
        def _set(self, instance, value):
            glDepthMask(GL_TRUE if value else GL_FALSE)
        add_descriptor("DepthMask", _get, _set)
    
    if hasattr(bgl, "glDepthRange"):
        from bgl import GL_DEPTH_RANGE, glDepthRange
        DepthRange = namedtuple("DepthRange", ("near", "far"))
        def _get(self, instance, owner):
            glGetFloatv(GL_DEPTH_RANGE, float2buf0)
            return DepthRange(*float2buf0)
        def _set(self, instance, value):
            glDepthRange(value[0], value[1])
        add_descriptor("DepthRange", _get, _set)
    
    if hasattr(bgl, "glPolygonOffset"):
        from bgl import GL_POLYGON_OFFSET_FACTOR, GL_POLYGON_OFFSET_UNITS, glPolygonOffset
        PolygonOffset = namedtuple("PolygonOffset", ("factor", "units"))
        def _get(self, instance, owner):
            glGetFloatv(GL_POLYGON_OFFSET_FACTOR, float2buf0)
            glGetFloatv(GL_POLYGON_OFFSET_UNITS, float2buf1)
            return PolygonOffset(float2buf0[0], float2buf1[0])
        def _set(self, instance, value):
            glPolygonOffset(value[0], value[1])
        add_descriptor("PolygonOffset", _get, _set)
    
    return {"cgl":cgl, "TextWrapper":TextWrapper}