File: types.py

package info (click to toggle)
python-moderngl-window 2.4.6-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 69,220 kB
  • sloc: python: 11,387; makefile: 21
file content (162 lines) | stat: -rw-r--r-- 4,716 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
Notes from moderngl:

The vao_content is a list of 3-tuples (buffer, format, attribs)
the format can have an empty or '/v', '/i', '/r' ending.
'/v' attributes are the default
'/i` attributes are per instance attributes
'/r' attributes are per render (like a uniform)
Example:
    vao_content = [
        (self.position_vertex_buffer, '2f', 'in_vert'),
        (self.color_buffer, '3f', 'in_color'),
        (self.pos_scale_buffer, '2f 1f/i', 'in_pos', 'in_scale'),
    ]
"""
import re
from functools import lru_cache
from typing import List

VALID_DIVISORS = ["v", "i", "r"]


class BufferFormat:
    def __init__(
        self,
        format_string: str,
        components: int,
        bytes_per_component: int,
        per_instance=False,
    ):
        """
        Args:
            format_string (str): moderngl format string
            components (int): components
            byte_size (int): bytes per component
            per_instance (bool): Instanced attribute
        """
        self.format = format_string
        self.components = components
        self.bytes_per_component = bytes_per_component
        self.per_instance = per_instance

    @property
    def bytes_total(self) -> int:
        """int: total byte size if this type"""
        return self.components * self.bytes_per_component

    def pad_str(self) -> str:
        """Padding string used my moderngl in interleaved buffers"""
        return "{}x{}".format(self.components, self.bytes_per_component)

    def __str__(self) -> str:
        return "<BufferFormat {} components={} bytes_per_component={}>".format(
            self.format, self.components, self.bytes_per_component
        )

    def __repr__(self) -> str:
        return str(self)


@lru_cache(maxsize=500)
def attribute_format(attr_format: str) -> BufferFormat:
    """Look up info about an attribute format.

    Translate the format into a BufferFormat instance
    containing things like byte size and components

    Args:
        buffer_format (str): Format of an attribute
    Returns:
        BufferFormat instance
    """
    if not attr_format:
        raise ValueError("Cannot resolve buffer format: '{}'".format(attr_format))

    parts = attr_format.split("/")

    # Parse out divisor if present
    # Examples: 3f/i, 3f/v
    fmt = parts[0]
    divisor = ""
    if len(parts) > 1:
        divisor = parts[1]
        if divisor not in VALID_DIVISORS:
            raise ValueError(
                "Invalid attribute divisor '{}' in '{}'".format(divisor, buffer_format)
            )

    # Parse out out component count and actual format
    parts = re.split(r"([fiudn])", fmt)
    components = 1
    if parts[0].isalnum():
        components = int(parts[0])
        bformat = fmt[len(parts[0]):]
    else:
        bformat = fmt

    # Construct specific buffer format
    fmt_info = buffer_format(bformat)
    return BufferFormat(
        "{}{}{}".format(components, bformat, "/{}".format(divisor) if divisor else ""),
        components,
        fmt_info.bytes_per_component,
        per_instance=divisor == "i",
    )


def parse_attribute_formats(frmt: str) -> List[BufferFormat]:
    return [attribute_format(attr) for attr in frmt.split()]


def buffer_format(frmt: str) -> BufferFormat:
    """Look up info about a buffer format type

    Args:
        frmt (str): format string such as 'f', 'i' and 'u'
    Returns:
        BufferFormat instance
    """
    try:
        return BUFFER_FORMATS[frmt]
    except KeyError:
        raise ValueError(
            "Buffer format '{}' unknown. Valid formats: {}".format(
                frmt, BUFFER_FORMATS.keys()
            )
        )


BUFFER_FORMATS = {
    # Floats
    "f": BufferFormat("f", 1, 4),
    "f1": BufferFormat("f1", 1, 1),
    "f2": BufferFormat("f2", 1, 2),
    "f4": BufferFormat("f4", 1, 4),
    "f8": BufferFormat("f8", 1, 8),
    # Unsigned Integers
    "u": BufferFormat("u", 1, 4),
    "u1": BufferFormat("u1", 1, 1),
    "u2": BufferFormat("u2", 1, 2),
    "u4": BufferFormat("u4", 1, 4),
    # Signed Integer
    "i": BufferFormat("i", 1, 4),
    "i1": BufferFormat("i1", 1, 1),
    "i2": BufferFormat("i2", 1, 2),
    "i4": BufferFormat("i4", 1, 4),
    # Normalized float
    "nf": BufferFormat("nf", 1, 4),
    "nf1": BufferFormat("nf1", 1, 1),
    "nf2": BufferFormat("nf2", 1, 2),
    "nf4": BufferFormat("nf4", 1, 4),
    # Normalized uint
    "nu": BufferFormat("nu", 1, 4),
    "nu1": BufferFormat("nu1", 1, 1),
    "nu2": BufferFormat("nu2", 1, 2),
    "nu4": BufferFormat("nu4", 1, 4),
    # Normalized int
    "ni": BufferFormat("ni", 1, 4),
    "ni1": BufferFormat("ni1", 1, 1),
    "ni2": BufferFormat("ni2", 1, 2),
    "ni4": BufferFormat("ni4", 1, 4),
}