コード例 #1
0
class FontRenderer():
    CHAR_DTYPE = np.dtype([
        ('position', np.float32, (2,)),
        ('color', np.float32, (4,)),
        ('size', np.float32),
        ('rot', np.float32),
        ('fntobj', np.int32),
    ])

    GLYPH_DTYPE = np.dtype([
        ('position', np.float32, (4,)),
        ('size', np.float32, (4,)),
        ('offset', np.float32, (4,)),
        ('xadvance', np.float32),
        ('page', np.float32),
        ('chnl', np.float32),
        ('buff', np.float32),
    ])

    def __init__(self, font=None):
        self.camera = None
        if font is None:
            font = os.path.join(FONT_FOLDER, 'arial.fnt')
        self.font_path = font
        self.texts = []
        self._has_changes = True
        self._fnt = None

    @property
    def font(self):
        return self._fnt


    def init(self):
        # load font file
        self._fnt = FNTFile.load_from_file(self.font_path)

        # upload glyph 3d atlas
        page_data         = [imread(img_path)[:,:,3] for img_path in self._fnt.page_paths]
        glypthatlas_width = page_data[0].shape[0]
        glyphatlas_height = page_data[0].shape[1]
        glyphatlas        = np.empty((len(page_data), glypthatlas_width, glyphatlas_height), dtype=np.float32)
        for i, glyphdata in enumerate(page_data):
            if glyphdata.shape[0:2] != (glypthatlas_width,glyphatlas_height):
                raise Exception((
                    'font "{}" corrupt: font page id={} file="{}" image size {}x{}'
                    + ' differs from the first page id=0 file="{}" {}x{}').format(
                        self.font, i, self._fnt.page_paths[i], glyphdata.shape[0],
                        glyphdata.shape[1], self._fnt.page_paths[0],
                        page_data[0].shape[0], page_data[0].shape[1]))

            glyphatlas[i] = glyphdata

        self.texture_id = glGenTextures(1);

        glBindTexture(GL_TEXTURE_2D_ARRAY, self.texture_id);
        glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R32F, glypthatlas_width, glyphatlas_height, glyphatlas.shape[0], 0, GL_RED, GL_FLOAT, glyphatlas);
        glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glBindTexture(GL_TEXTURE_2D_ARRAY, 0);

        # create shader
        self.shader_program = Program()
        self.shader_program.shaders.append(Shader(GL_VERTEX_SHADER, VERTEX_SHADER))
        self.shader_program.shaders.append(Shader(GL_GEOMETRY_SHADER, GEOMETRY_SHADER.replace('$n$', str(len(self._fnt.glyphs)))))
        self.shader_program.shaders.append(Shader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER.replace('$n$', str(len(self._fnt.glyphs)))))
        self.shader_program.link()

        # create vbo/vao
        self.buffer = BufferObject.empty(1, dtype=self.CHAR_DTYPE)
        self.vao = glGenVertexArrays(1)

        # bind vao
        glBindVertexArray(self.vao)
        self.buffer.bind()

        itemsize = c_int(self.CHAR_DTYPE.itemsize)
        glVertexAttribPointer(self.shader_program.attributes['vertex_position'], 2, GL_FLOAT, GL_FALSE, itemsize, c_void_p(0))
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(self.shader_program.attributes['glyph_color'], 4, GL_FLOAT, GL_FALSE, itemsize, c_void_p(8))
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(self.shader_program.attributes['glyph_size'], 1, GL_FLOAT, GL_FALSE, itemsize, c_void_p(24))
        glEnableVertexAttribArray(2)
        glVertexAttribPointer(self.shader_program.attributes['glyph_rot'], 1, GL_FLOAT, GL_FALSE, itemsize, c_void_p(28))
        glEnableVertexAttribArray(3)
        glVertexAttribIPointer(self.shader_program.attributes['fntobj'], 1, GL_INT, itemsize, c_void_p(32))
        glEnableVertexAttribArray(4)

        self.buffer.unbind()
        glBindVertexArray(0)

        # upload glyph information
        glyphdata = np.empty(len(self._fnt.glyphs), dtype=self.GLYPH_DTYPE)
        glyphdata[:] = [c.dump() for c in self._fnt.glyphs]

        self.font_buffer = BufferObject.to_device(glyphdata, target=GL_UNIFORM_BUFFER)
        self.font_buffer.bind_buffer_base(0)

        block_index = glGetUniformBlockIndex(self.shader_program.gl_id, "ubo_font_objects")
        if block_index == GL_INVALID_INDEX:
            raise Exception('wo is der block was')

        glUniformBlockBinding(self.shader_program.gl_id, block_index, self.font_buffer.gl_buffer_base)

        self.shader_program.uniform('tex_scale', [1.0/glypthatlas_width, 1.0/glyphatlas_height])
        self.shader_program.uniform('fontsize_real', 60)
        self.shader_program.uniform('tex', 1)

        if self.camera:
            self.shader_program.uniform('mat_camera', self.camera.get_matrix())
            self.camera_changed = False

        for text in self.texts:
            text.font = self._fnt

    def set_camera(self, camera):
        self.camera = camera
        self.camera_changed = True

    def create_text(self, text, size=10, position=(0,0), color=[0,0,0,1], rotation=0, **kwargs):
        """
        creates a text with certain size, position and rotation.
        :text: the text to be rendered
        :size: size of the text
        :position: position on xy plane
        :rotation: angle to rotate aroung (position.x, position.y)
           in mathematical way (anti clockwise).
           np.pi/2   = 90°
           np.pi     = 180°
           np.pi*3/2 = 270°
           2*pi      = 360°
        returns a TextObject instance.
        """
        textobj = TextObject(self, text, size, position, color=color, rotation=rotation, **kwargs)
        self.texts.append(textobj)
        self._has_changes = True
        return textobj

    def clear_texts(self):
        self.texts = []
        self._has_changes = True

    def render(self):
        self._dummybufferstuff()
        if self.camera_changed:
            self.shader_program.uniform('mat_camera', self.camera.get_matrix())

        # prepare
        glClearColor(1,1,1,1)
        glActiveTexture(GL_TEXTURE1) # XXX disale texture later??
        glBindTexture (GL_TEXTURE_2D_ARRAY, int(self.texture_id))
        glEnable (GL_BLEND)
        glBlendFunc (GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA)

        # render
        glBindVertexArray(self.vao)
        self.shader_program.use()
        glDrawArrays(GL_POINTS, 0, self.buffer.shape[0])
        self.shader_program.unuse()
        glBindVertexArray(0)

    def _dummybufferstuff(self):
        # XXX
        #  think about me,
        #  intelligent partitioning and bla
        if self._has_changes:
            nchars = 0
            for textobj in self.texts:
                nchars += len(textobj)

            chardata = np.empty(nchars, dtype=self.CHAR_DTYPE)
            index = 0
            for textobj in self.texts:
                data = textobj.get_data()
                chardata[index:index+len(data)] = data
                index += len(data)

            self.buffer.set(chardata)
            self._has_changes = False
コード例 #2
0
class Field:
    """
    draw a 2d field by using Field Domain (OpenGL textures).

    this plot runs either with or without a given FieldDomain(texture).
    If no domain defined, the field will scale to top_left, bottom_right.
    the data_kernel can be used to calculate data where domain is [0,1] x [0,1].

    if domain is not None, that top_left, bottom_right will be the center of
    top_left and bottom_right texel. data_kernel is by default the default
    glsl 2d texture sampler.

    using a color scheme one can modify data from data_kernel.
    (field plot color scheme...)
    """

    def __init__(self, domain=None, top_left=None, bottom_right=None, color_scheme=None, data_kernel=None):

        if domain is None and data_kernel is None:
            raise ValueError("either domain or data_kernel must be defined.")

        self.top_left = top_left or None
        self.bottom_right = bottom_right or None
        self.domain = domain
        self.color_scheme = color_scheme or ""
        self.initialized = False
        self.program = None
        self.data_kernel = data_kernel or "fragment_color = texture(tex[0], x);"
        self._np_vertex_data = None
        self._np_texture_data = None
        self._coord_top_left = None
        self._coord_bottom_right = None

    def init(self):
        if self.domain is not None:
            if not hasattr(self.domain, "dimensions"):
                raise ValueError("domain must have attribute domain.dimensions")
            if not hasattr(self.domain, "gl_texture_id"):
                raise ValueError("domain must have attribute domain.gl_texture_id")

            self.domain.gl_init()
            if self.top_left is None:
                self.top_left = (1, self.domain.dimensions[1])
            if self.bottom_right is None:
                self.bottom_right = (self.domain.dimensions[0], 1)

        self.gl_init()

        self._np_texture_data = np.array([0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], dtype=np.float32).reshape(6, 2)
        self.scale(self.top_left, self.bottom_right)

        self.initialized = True

    def scale(self, top_left, bottom_right):
        """
        scales to field to a rectangle with top_left
        and bottom_right corner
        """
        if self.domain is None:
            self._coord_top_left = top_left or (0, 1)
            self._coord_bottom_right = bottom_right or (1, 0)
        else:
            dimensions = (self.domain.dimensions[0] - 1, self.domain.dimensions[1] - 1)
            unit_size_x = 0.5 * float(bottom_right[0] - top_left[0]) / dimensions[1]
            unit_size_y = 0.5 * float(top_left[1] - bottom_right[1]) / dimensions[0]
            self._coord_top_left = (top_left[0] - unit_size_x, top_left[1] + unit_size_y)
            self._coord_bottom_right = (bottom_right[0] + unit_size_x, bottom_right[1] - unit_size_y)

        self._np_vertex_data = np.array(
            [
                self._coord_top_left[0],
                self._coord_top_left[1],
                self._coord_top_left[0],
                self._coord_bottom_right[1],
                self._coord_bottom_right[0],
                self._coord_bottom_right[1],
                self._coord_bottom_right[0],
                self._coord_bottom_right[1],
                self._coord_bottom_right[0],
                self._coord_top_left[1],
                self._coord_top_left[0],
                self._coord_top_left[1],
            ],
            dtype=np.float32,
        ).reshape(6, 2)

        self.vertex_array = VertexArray(
            {
                "vertex_position": VertexBuffer.from_numpy(self._np_vertex_data),
                "texture_position": VertexBuffer.from_numpy(self._np_texture_data),
            },
            self.program.attributes,
        )

        self.top_left = top_left
        self.bottom_right = bottom_right

    def gl_init(self):
        """
        initializes shader program and plane vao
        """
        glsl_functions = ""
        if hasattr(self.color_scheme, "glsl_functions"):
            glsl_functions += "\n/*COLOR_SCHEME FUNCTIONS*/\n"
            glsl_functions += self.color_scheme.glsl_functions
        if hasattr(self.data_kernel, "glsl_functions"):
            glsl_functions += "\n/*DATA_KERNEL FUNCTIONS*/\n"
            glsl_functions += self.data_kernel.glsl_functions

        glsl_uniforms = []
        if hasattr(self.color_scheme, "glsl_uniforms"):
            glsl_uniforms.append("/*COLOR_SCHEME UNIFORMS*/")
            glsl_uniforms += ["uniform {} {};".format(t, n) for t, n in self.color_scheme.glsl_uniforms]
        if hasattr(self.data_kernel, "glsl_uniforms"):
            glsl_uniforms.append("/*DATA_KERNEL UNIFORMS*/")
            glsl_uniforms += ["uniform {} {};".format(t, n) for t, n in self.data_kernel.glsl_uniforms]

        frag_src = pystache.render(
            load_lib_file("glsl/plot2d/field.frag.glsl"),
            {
                "UNIFORMS": "\n".join(glsl_uniforms),
                "FUNCTIONS": glsl_functions,
                "DATA_KERNEL": str(self.data_kernel),
                "COLOR_KERNEL": str(self.color_scheme),
            },
        )

        self.program = Program()
        self.program.shaders.append(Shader(GL_VERTEX_SHADER, load_lib_file("glsl/plot2d/field.vert.glsl")))
        self.program.shaders.append(Shader(GL_FRAGMENT_SHADER, frag_src))
        self.program.link()

        if hasattr(self.color_scheme, "get_uniform_data"):
            for uniform in self.color_scheme.get_uniform_data().items():
                self.program.uniform(*uniform)
        if hasattr(self.data_kernel, "get_uniform_data"):
            for uniform in self.data_kernel.get_uniform_data().items():
                self.program.uniform(*uniform)
        self.initialized = True

    def update_plotmeta(self, plot_cam, outer_cam, *args, **kwargs):
        # not so nice ... but later refactoring ...
        self.program.uniform("mat_domain", np.identity(3))
        self.program.uniform("mat_camera", plot_cam)
        self.program.uniform("mat_outer_camera", outer_cam)
        if hasattr(self.color_scheme, "get_uniform_data"):
            for uniform in self.color_scheme.get_uniform_data().items():
                self.program.uniform(*uniform)
        if hasattr(self.data_kernel, "get_uniform_data"):
            for uniform in self.data_kernel.get_uniform_data().items():
                self.program.uniform(*uniform)

    def render(self, plotter):
        # final rendering
        glActiveTexture(GL_TEXTURE0)
        if self.domain is not None:
            glBindTexture(GL_TEXTURE_2D, self.domain.gl_texture_id)
        else:
            glBindTexture(GL_TEXTURE_2D, 0)

        self.vertex_array.bind()
        self.program.use()
        glDrawArrays(GL_TRIANGLES, 0, 6)
        self.vertex_array.unbind()
        self.program.unuse()

    @classmethod
    def create_texture1f(cls, size):
        texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texture)
        glTexImage2D(
            GL_TEXTURE_2D,
            0,
            GL_RED,
            size[0],
            size[1],
            0,
            GL_RED,
            GL_FLOAT,
            np.zeros(size[0] * size[1], dtype=np.float32),
        )
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        glBindTexture(GL_TEXTURE_2D, 0)
        return texture