Completed
Push — master ( 67fa70...e887f8 )
by
unknown
01:02
created

ed2d.load_image_hdr()   A

Complexity

Conditions 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 11
rs 9.4286
1
2
from PIL import Image
3
4
from ed2d.opengl import gl, pgl
5
6
from ed2d.assets import hdr
7
8
9
def load_image(path):
10
    img = Image.open(path)
11
12
    # Verify that the image is in RGBA format
13
    if ''.join(img.getbands()) != 'RGBA':
14
        img = img.convert('RGBA')
15
16
    # Get image data as a list
17
    data = list(img.getdata())
18
19
    width, height = img.size
20
    return width, height, data
21
22
def load_image_hdr(path):
23
    myhdr = hdr.HDR()
24
    myhdrloader = hdr.HDRLoader()
25
    myhdrloader.load(path, myhdr)
26
27
    test = []
28
29
    for i in range(len(myhdr.cols) / 3):
30
        test.append([myhdr.cols[i], myhdr.cols[i + 1], myhdr.cols[i + 2]])
31
32
    return myhdr.width, myhdr.height, test
33
34
35
class BaseTexture(object):
36
    ''' Texture manager'''
37
    # Just for clarity
38
    # This is basically a static variable
39
    # It will be for assigning each texture unit id easily.
40
    # We use the id for calculating GL_TEXTUREi. This value propagates
41
    # down into the class instances also, even as it is changed. :D
42
    #
43
    # edit: decide if this would be better off as an __init__ method that
44
    # the subclasses call via super. Would be the best thing if we want to
45
    # have external subclasses of the BaseTexture.
46
47
    _textureCount = 0
48
49
    def _set_unit_id(self):
50
        self.texUnitID = self._textureCount
51
        BaseTexture._textureCount += 1
52
53
    def __init__(self, program):
54
55
        self.program = program
56
        self.texFormat = None
57
58
        self._set_unit_id()
59
60
    def load_gl(self):
61
62
        self.texSampID = self.program.new_uniform(b'textureSampler')
63
        self.texResID = self.program.new_uniform(b'textureResolution')
64
65
        if self.texFormat is None:
66
            self.texFormat = gl.GL_RGBA
67
68
        # Load image into new opengl texture
69
        self.texID = pgl.glGenTextures(1)
70
        gl.glBindTexture(gl.GL_TEXTURE_2D, self.texID)
71
        gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
72
73
        gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
74
        gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
75
76
        pgl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA16F, self.width, self.height,
77
                         0, self.texFormat, gl.GL_FLOAT, self.data)
78
79
    def bind(self):
80
        gl.glActiveTexture(gl.GL_TEXTURE0 + self.texUnitID)
81
        gl.glBindTexture(gl.GL_TEXTURE_2D, self.texID)
82
        self.program.set_uniform(self.texSampID, self.texUnitID)
83
84
85
class Texture(BaseTexture):
86
87
    def __init__(self, path, program, texFormat=None):
88
        super(Texture, self).__init__(program)
89
        self.path = path
90
91
        self.texFormat = texFormat
92
93
        self.width, self.height, self.data = load_image_hdr(self.path)
94
95
        self.load_gl()
96
97
    def load_gl(self):
98
        super(Texture, self).load_gl()
99
        self.program.set_uniform_array(self.texResID, [float(self.width), float(self.height)])
100
101
102
class TextureAtlas(BaseTexture):
103
    def __init__(self, program, maxWidth=1024, texFormat=None):
104
        super(TextureAtlas, self).__init__(program)
105
106
        self.program = program
107
108
        self.texFormat = texFormat
109
110
        # Data format will be as follows:
111
        #    Indexed by textureID
112
        #    value:
113
        #        - a second dict with information about that texture
114
        #            - x, y position in texture, width and height, texture
115
        #              data/uvcoords
116
        self.textures = []
117
        self.data = 0
118
        self.maxWidth = maxWidth
119
120
        self.width = 0
121
        self.height = 0
122
        self.cursorPosY = 0
123
        self.cursorPosX = 0
124
        self.lineHeight = 0
125
        self.maxSubTextureHeight = 0
126
127
    def add_texture(self, width, height, texData):
128
        textureID = len(self.textures)
129
130
        imgWidth = width
131
        imgHeight = height
132
133
        if (self.cursorPosX + imgWidth + 1) >= self.maxWidth:
134
135
            self.width = max(self.width, self.cursorPosX)
136
            self.cursorPosY += self.lineHeight
137
138
            self.maxSubTextureHeight = max(self.maxSubTextureHeight, self.lineHeight - 1)
139
140
            self.lineHeight = 0
141
            self.cursorPosX = 0
142
143
        x1 = self.cursorPosX
144
        x2 = self.cursorPosX + imgWidth
145
        y1 = self.cursorPosY
146
        y2 = self.cursorPosY + imgHeight
147
148
        self.cursorPosX += imgWidth + 1
149
        self.lineHeight = max(self.lineHeight, imgHeight + 1)
150
151
        self.textures.append({
152
                'x1': x1, 'x2': x2, 'y1': y1, 'y2': y2,
153
                'width': width, 'height': height,
154
                'texData': texData, 'uvCoords': None,
155
        })
156
        return textureID
157
158
    def get_uvcoords(self, texID):
159
160
        tex = self.textures[texID]
161
162
        x1 = tex['x1'] / float(self.width)
163
        x2 = tex['x2'] / float(self.width)
164
        y1 = tex['y1'] / float(self.height)
165
        y2 = tex['y2'] / float(self.height)
166
167
        coord = [[x1, y2],
168
                 [x2, y2],
169
                 [x1, y1],
170
                 [x2, y1]]
171
172
        return coord
173
174
    def get_vertex_scale(self, texID):
175
        '''
176
        Returns calculated vertex scaleing for textures by textureID
177
        This nomalizes all subtextures to the height of the tallest texture.
178
        This is done because the vertex data sent to the gpu is the same for
179
        each
180
        '''
181
182
        tex = self.textures[texID]
183
184
        imgWidth = tex['width']
185
        imgHeight = tex['height']
186
187
        vertScaleY = imgHeight / float(self.maxSubTextureHeight)
188
        vertScaleX = imgWidth / float(self.maxSubTextureHeight)
189
190
        return (vertScaleX, vertScaleY)
191
192
    def gen_atlas(self):
193
194
        self.cursorPosY += self.lineHeight
195
196
        self.width = max(self.width, self.cursorPosX)
197
        self.height = self.cursorPosY
198
199
        self.maxSubTextureHeight = max(self.maxSubTextureHeight, self.lineHeight - 1)
200
201
        self.load_gl()
202
203
        # add data to blank gl texture with
204
        # glTexSubImage2D here
205
        for tex in self.textures:
206
            x1 = tex['x1']
207
            y1 = tex['y1']
208
            width = tex['width']
209
            height = tex['height']
210
            texData = tex['texData']
211
212
            pgl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, x1, y1, width, height, self.texFormat, gl.GL_UNSIGNED_BYTE, texData)
213