pyrender-maintained 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyrender/__init__.py +24 -0
- pyrender/camera.py +435 -0
- pyrender/constants.py +149 -0
- pyrender/font.py +272 -0
- pyrender/light.py +382 -0
- pyrender/material.py +705 -0
- pyrender/mesh.py +328 -0
- pyrender/node.py +263 -0
- pyrender/offscreen.py +160 -0
- pyrender/platforms/__init__.py +6 -0
- pyrender/platforms/base.py +73 -0
- pyrender/platforms/egl.py +219 -0
- pyrender/platforms/osmesa.py +59 -0
- pyrender/platforms/pyglet_platform.py +90 -0
- pyrender/primitive.py +489 -0
- pyrender/renderer.py +1328 -0
- pyrender/sampler.py +102 -0
- pyrender/scene.py +585 -0
- pyrender/shader_program.py +283 -0
- pyrender/texture.py +259 -0
- pyrender/trackball.py +216 -0
- pyrender/utils.py +115 -0
- pyrender/version.py +1 -0
- pyrender/viewer.py +1157 -0
- pyrender_maintained-1.0.0.dist-info/METADATA +55 -0
- pyrender_maintained-1.0.0.dist-info/RECORD +29 -0
- pyrender_maintained-1.0.0.dist-info/WHEEL +5 -0
- pyrender_maintained-1.0.0.dist-info/licenses/LICENSE +21 -0
- pyrender_maintained-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""OpenGL shader program wrapper.
|
|
2
|
+
"""
|
|
3
|
+
import numpy as np
|
|
4
|
+
import os
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
import OpenGL
|
|
8
|
+
from OpenGL.GL import *
|
|
9
|
+
from OpenGL.GL import shaders as gl_shader_utils
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ShaderProgramCache(object):
|
|
13
|
+
"""A cache for shader programs.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, shader_dir=None):
|
|
17
|
+
self._program_cache = {}
|
|
18
|
+
self.shader_dir = shader_dir
|
|
19
|
+
if self.shader_dir is None:
|
|
20
|
+
base_dir, _ = os.path.split(os.path.realpath(__file__))
|
|
21
|
+
self.shader_dir = os.path.join(base_dir, 'shaders')
|
|
22
|
+
|
|
23
|
+
def get_program(self, vertex_shader, fragment_shader,
|
|
24
|
+
geometry_shader=None, defines=None):
|
|
25
|
+
"""Get a program via a list of shader files to include in the program.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
vertex_shader : str
|
|
30
|
+
The vertex shader filename.
|
|
31
|
+
fragment_shader : str
|
|
32
|
+
The fragment shader filename.
|
|
33
|
+
geometry_shader : str
|
|
34
|
+
The geometry shader filename.
|
|
35
|
+
defines : dict
|
|
36
|
+
Defines and their values for the shader.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
program : :class:`.ShaderProgram`
|
|
41
|
+
The program.
|
|
42
|
+
"""
|
|
43
|
+
shader_names = []
|
|
44
|
+
if defines is None:
|
|
45
|
+
defines = {}
|
|
46
|
+
shader_filenames = [
|
|
47
|
+
x for x in [vertex_shader, fragment_shader, geometry_shader]
|
|
48
|
+
if x is not None
|
|
49
|
+
]
|
|
50
|
+
for fn in shader_filenames:
|
|
51
|
+
if fn is None:
|
|
52
|
+
continue
|
|
53
|
+
_, name = os.path.split(fn)
|
|
54
|
+
shader_names.append(name)
|
|
55
|
+
cid = OpenGL.contextdata.getContext()
|
|
56
|
+
key = tuple([cid] + sorted(
|
|
57
|
+
[(s,1) for s in shader_names] + [(d, defines[d]) for d in defines]
|
|
58
|
+
))
|
|
59
|
+
|
|
60
|
+
if key not in self._program_cache:
|
|
61
|
+
shader_filenames = [
|
|
62
|
+
os.path.join(self.shader_dir, fn) for fn in shader_filenames
|
|
63
|
+
]
|
|
64
|
+
if len(shader_filenames) == 2:
|
|
65
|
+
shader_filenames.append(None)
|
|
66
|
+
vs, fs, gs = shader_filenames
|
|
67
|
+
self._program_cache[key] = ShaderProgram(
|
|
68
|
+
vertex_shader=vs, fragment_shader=fs,
|
|
69
|
+
geometry_shader=gs, defines=defines
|
|
70
|
+
)
|
|
71
|
+
return self._program_cache[key]
|
|
72
|
+
|
|
73
|
+
def clear(self):
|
|
74
|
+
for key in self._program_cache:
|
|
75
|
+
self._program_cache[key].delete()
|
|
76
|
+
self._program_cache = {}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ShaderProgram(object):
|
|
80
|
+
"""A thin wrapper about OpenGL shader programs that supports easy creation,
|
|
81
|
+
binding, and uniform-setting.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
vertex_shader : str
|
|
86
|
+
The vertex shader filename.
|
|
87
|
+
fragment_shader : str
|
|
88
|
+
The fragment shader filename.
|
|
89
|
+
geometry_shader : str
|
|
90
|
+
The geometry shader filename.
|
|
91
|
+
defines : dict
|
|
92
|
+
Defines and their values for the shader.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def __init__(self, vertex_shader, fragment_shader,
|
|
96
|
+
geometry_shader=None, defines=None):
|
|
97
|
+
|
|
98
|
+
self.vertex_shader = vertex_shader
|
|
99
|
+
self.fragment_shader = fragment_shader
|
|
100
|
+
self.geometry_shader = geometry_shader
|
|
101
|
+
|
|
102
|
+
self.defines = defines
|
|
103
|
+
if self.defines is None:
|
|
104
|
+
self.defines = {}
|
|
105
|
+
|
|
106
|
+
self._program_id = None
|
|
107
|
+
self._vao_id = None # PYOPENGL BUG
|
|
108
|
+
|
|
109
|
+
# DEBUG
|
|
110
|
+
# self._unif_map = {}
|
|
111
|
+
|
|
112
|
+
def _add_to_context(self):
|
|
113
|
+
if self._program_id is not None:
|
|
114
|
+
raise ValueError('Shader program already in context')
|
|
115
|
+
shader_ids = []
|
|
116
|
+
|
|
117
|
+
# Load vert shader
|
|
118
|
+
shader_ids.append(gl_shader_utils.compileShader(
|
|
119
|
+
self._load(self.vertex_shader), GL_VERTEX_SHADER)
|
|
120
|
+
)
|
|
121
|
+
# Load frag shader
|
|
122
|
+
shader_ids.append(gl_shader_utils.compileShader(
|
|
123
|
+
self._load(self.fragment_shader), GL_FRAGMENT_SHADER)
|
|
124
|
+
)
|
|
125
|
+
# Load geometry shader
|
|
126
|
+
if self.geometry_shader is not None:
|
|
127
|
+
shader_ids.append(gl_shader_utils.compileShader(
|
|
128
|
+
self._load(self.geometry_shader), GL_GEOMETRY_SHADER)
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Bind empty VAO PYOPENGL BUG
|
|
132
|
+
if self._vao_id is None:
|
|
133
|
+
self._vao_id = glGenVertexArrays(1)
|
|
134
|
+
glBindVertexArray(self._vao_id)
|
|
135
|
+
|
|
136
|
+
# Compile program
|
|
137
|
+
self._program_id = gl_shader_utils.compileProgram(*shader_ids)
|
|
138
|
+
|
|
139
|
+
# Unbind empty VAO PYOPENGL BUG
|
|
140
|
+
glBindVertexArray(0)
|
|
141
|
+
|
|
142
|
+
def _in_context(self):
|
|
143
|
+
return self._program_id is not None
|
|
144
|
+
|
|
145
|
+
def _remove_from_context(self):
|
|
146
|
+
if self._program_id is not None:
|
|
147
|
+
glDeleteProgram(self._program_id)
|
|
148
|
+
glDeleteVertexArrays(1, [self._vao_id])
|
|
149
|
+
self._program_id = None
|
|
150
|
+
self._vao_id = None
|
|
151
|
+
|
|
152
|
+
def _load(self, shader_filename):
|
|
153
|
+
path, _ = os.path.split(shader_filename)
|
|
154
|
+
|
|
155
|
+
with open(shader_filename) as f:
|
|
156
|
+
text = f.read()
|
|
157
|
+
|
|
158
|
+
def ifdef(matchobj):
|
|
159
|
+
if matchobj.group(1) in self.defines:
|
|
160
|
+
return '#if 1'
|
|
161
|
+
else:
|
|
162
|
+
return '#if 0'
|
|
163
|
+
|
|
164
|
+
def ifndef(matchobj):
|
|
165
|
+
if matchobj.group(1) in self.defines:
|
|
166
|
+
return '#if 0'
|
|
167
|
+
else:
|
|
168
|
+
return '#if 1'
|
|
169
|
+
|
|
170
|
+
ifdef_regex = re.compile(
|
|
171
|
+
'#ifdef\\s+([a-zA-Z_][a-zA-Z_0-9]*)\\s*$', re.MULTILINE
|
|
172
|
+
)
|
|
173
|
+
ifndef_regex = re.compile(
|
|
174
|
+
'#ifndef\\s+([a-zA-Z_][a-zA-Z_0-9]*)\\s*$', re.MULTILINE
|
|
175
|
+
)
|
|
176
|
+
text = re.sub(ifdef_regex, ifdef, text)
|
|
177
|
+
text = re.sub(ifndef_regex, ifndef, text)
|
|
178
|
+
|
|
179
|
+
for define in self.defines:
|
|
180
|
+
value = str(self.defines[define])
|
|
181
|
+
text = text.replace(define, value)
|
|
182
|
+
|
|
183
|
+
return text
|
|
184
|
+
|
|
185
|
+
def _bind(self):
|
|
186
|
+
"""Bind this shader program to the current OpenGL context.
|
|
187
|
+
"""
|
|
188
|
+
if self._program_id is None:
|
|
189
|
+
raise ValueError('Cannot bind program that is not in context')
|
|
190
|
+
# glBindVertexArray(self._vao_id)
|
|
191
|
+
glUseProgram(self._program_id)
|
|
192
|
+
|
|
193
|
+
def _unbind(self):
|
|
194
|
+
"""Unbind this shader program from the current OpenGL context.
|
|
195
|
+
"""
|
|
196
|
+
glUseProgram(0)
|
|
197
|
+
|
|
198
|
+
def delete(self):
|
|
199
|
+
"""Delete this shader program from the current OpenGL context.
|
|
200
|
+
"""
|
|
201
|
+
self._remove_from_context()
|
|
202
|
+
|
|
203
|
+
def set_uniform(self, name, value, unsigned=False):
|
|
204
|
+
"""Set a uniform value in the current shader program.
|
|
205
|
+
|
|
206
|
+
Parameters
|
|
207
|
+
----------
|
|
208
|
+
name : str
|
|
209
|
+
Name of the uniform to set.
|
|
210
|
+
value : int, float, or ndarray
|
|
211
|
+
Value to set the uniform to.
|
|
212
|
+
unsigned : bool
|
|
213
|
+
If True, ints will be treated as unsigned values.
|
|
214
|
+
"""
|
|
215
|
+
try:
|
|
216
|
+
# DEBUG
|
|
217
|
+
# self._unif_map[name] = 1, (1,)
|
|
218
|
+
loc = glGetUniformLocation(self._program_id, name)
|
|
219
|
+
|
|
220
|
+
if loc == -1:
|
|
221
|
+
raise ValueError('Invalid shader variable: {}'.format(name))
|
|
222
|
+
|
|
223
|
+
if isinstance(value, np.ndarray):
|
|
224
|
+
# DEBUG
|
|
225
|
+
# self._unif_map[name] = value.size, value.shape
|
|
226
|
+
if value.ndim == 1:
|
|
227
|
+
if (np.issubdtype(value.dtype, np.unsignedinteger) or
|
|
228
|
+
unsigned):
|
|
229
|
+
dtype = 'u'
|
|
230
|
+
value = value.astype(np.uint32)
|
|
231
|
+
elif np.issubdtype(value.dtype, np.integer):
|
|
232
|
+
dtype = 'i'
|
|
233
|
+
value = value.astype(np.int32)
|
|
234
|
+
else:
|
|
235
|
+
dtype = 'f'
|
|
236
|
+
value = value.astype(np.float32)
|
|
237
|
+
self._FUNC_MAP[(value.shape[0], dtype)](loc, 1, value)
|
|
238
|
+
else:
|
|
239
|
+
self._FUNC_MAP[(value.shape[0], value.shape[1])](
|
|
240
|
+
loc, 1, GL_TRUE, value
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Call correct uniform function
|
|
244
|
+
elif isinstance(value, float):
|
|
245
|
+
glUniform1f(loc, value)
|
|
246
|
+
elif isinstance(value, int):
|
|
247
|
+
if unsigned:
|
|
248
|
+
glUniform1ui(loc, value)
|
|
249
|
+
else:
|
|
250
|
+
glUniform1i(loc, value)
|
|
251
|
+
elif isinstance(value, bool):
|
|
252
|
+
if unsigned:
|
|
253
|
+
glUniform1ui(loc, int(value))
|
|
254
|
+
else:
|
|
255
|
+
glUniform1i(loc, int(value))
|
|
256
|
+
else:
|
|
257
|
+
raise ValueError('Invalid data type')
|
|
258
|
+
except Exception:
|
|
259
|
+
pass
|
|
260
|
+
|
|
261
|
+
_FUNC_MAP = {
|
|
262
|
+
(1,'u'): glUniform1uiv,
|
|
263
|
+
(2,'u'): glUniform2uiv,
|
|
264
|
+
(3,'u'): glUniform3uiv,
|
|
265
|
+
(4,'u'): glUniform4uiv,
|
|
266
|
+
(1,'i'): glUniform1iv,
|
|
267
|
+
(2,'i'): glUniform2iv,
|
|
268
|
+
(3,'i'): glUniform3iv,
|
|
269
|
+
(4,'i'): glUniform4iv,
|
|
270
|
+
(1,'f'): glUniform1fv,
|
|
271
|
+
(2,'f'): glUniform2fv,
|
|
272
|
+
(3,'f'): glUniform3fv,
|
|
273
|
+
(4,'f'): glUniform4fv,
|
|
274
|
+
(2,2): glUniformMatrix2fv,
|
|
275
|
+
(2,3): glUniformMatrix2x3fv,
|
|
276
|
+
(2,4): glUniformMatrix2x4fv,
|
|
277
|
+
(3,2): glUniformMatrix3x2fv,
|
|
278
|
+
(3,3): glUniformMatrix3fv,
|
|
279
|
+
(3,4): glUniformMatrix3x4fv,
|
|
280
|
+
(4,2): glUniformMatrix4x2fv,
|
|
281
|
+
(4,3): glUniformMatrix4x3fv,
|
|
282
|
+
(4,4): glUniformMatrix4fv,
|
|
283
|
+
}
|
pyrender/texture.py
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"""Textures, conforming to the glTF 2.0 standards as specified in
|
|
2
|
+
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-texture
|
|
3
|
+
|
|
4
|
+
Author: Matthew Matl
|
|
5
|
+
"""
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from OpenGL.GL import *
|
|
9
|
+
|
|
10
|
+
from .utils import format_texture_source
|
|
11
|
+
from .sampler import Sampler
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Texture(object):
|
|
15
|
+
"""A texture and its sampler.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
name : str, optional
|
|
20
|
+
The user-defined name of this object.
|
|
21
|
+
sampler : :class:`Sampler`
|
|
22
|
+
The sampler used by this texture.
|
|
23
|
+
source : (h,w,c) uint8 or (h,w,c) float or :class:`PIL.Image.Image`
|
|
24
|
+
The image used by this texture. If None, the texture is created
|
|
25
|
+
empty and width and height must be specified.
|
|
26
|
+
source_channels : str
|
|
27
|
+
Either `D`, `R`, `RG`, `GB`, `RGB`, or `RGBA`. Indicates the
|
|
28
|
+
channels to extract from `source`. Any missing channels will be filled
|
|
29
|
+
with `1.0`.
|
|
30
|
+
width : int, optional
|
|
31
|
+
For empty textures, the width of the texture buffer.
|
|
32
|
+
height : int, optional
|
|
33
|
+
For empty textures, the height of the texture buffer.
|
|
34
|
+
tex_type : int
|
|
35
|
+
Either GL_TEXTURE_2D or GL_TEXTURE_CUBE.
|
|
36
|
+
data_format : int
|
|
37
|
+
For now, just GL_FLOAT.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self,
|
|
41
|
+
name=None,
|
|
42
|
+
sampler=None,
|
|
43
|
+
source=None,
|
|
44
|
+
source_channels=None,
|
|
45
|
+
width=None,
|
|
46
|
+
height=None,
|
|
47
|
+
tex_type=GL_TEXTURE_2D,
|
|
48
|
+
data_format=GL_UNSIGNED_BYTE):
|
|
49
|
+
self.source_channels = source_channels
|
|
50
|
+
self.name = name
|
|
51
|
+
self.sampler = sampler
|
|
52
|
+
self.source = source
|
|
53
|
+
self.width = width
|
|
54
|
+
self.height = height
|
|
55
|
+
self.tex_type = tex_type
|
|
56
|
+
self.data_format = data_format
|
|
57
|
+
|
|
58
|
+
self._texid = None
|
|
59
|
+
self._is_transparent = False
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def name(self):
|
|
63
|
+
"""str : The user-defined name of this object.
|
|
64
|
+
"""
|
|
65
|
+
return self._name
|
|
66
|
+
|
|
67
|
+
@name.setter
|
|
68
|
+
def name(self, value):
|
|
69
|
+
if value is not None:
|
|
70
|
+
value = str(value)
|
|
71
|
+
self._name = value
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def sampler(self):
|
|
75
|
+
""":class:`Sampler` : The sampler used by this texture.
|
|
76
|
+
"""
|
|
77
|
+
return self._sampler
|
|
78
|
+
|
|
79
|
+
@sampler.setter
|
|
80
|
+
def sampler(self, value):
|
|
81
|
+
if value is None:
|
|
82
|
+
value = Sampler()
|
|
83
|
+
self._sampler = value
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def source(self):
|
|
87
|
+
"""(h,w,c) uint8 or float or :class:`PIL.Image.Image` : The image
|
|
88
|
+
used in this texture.
|
|
89
|
+
"""
|
|
90
|
+
return self._source
|
|
91
|
+
|
|
92
|
+
@source.setter
|
|
93
|
+
def source(self, value):
|
|
94
|
+
if value is None:
|
|
95
|
+
self._source = None
|
|
96
|
+
else:
|
|
97
|
+
self._source = format_texture_source(value, self.source_channels)
|
|
98
|
+
self._is_transparent = False
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def source_channels(self):
|
|
102
|
+
"""str : The channels that were extracted from the original source.
|
|
103
|
+
"""
|
|
104
|
+
return self._source_channels
|
|
105
|
+
|
|
106
|
+
@source_channels.setter
|
|
107
|
+
def source_channels(self, value):
|
|
108
|
+
self._source_channels = value
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def width(self):
|
|
112
|
+
"""int : The width of the texture buffer.
|
|
113
|
+
"""
|
|
114
|
+
return self._width
|
|
115
|
+
|
|
116
|
+
@width.setter
|
|
117
|
+
def width(self, value):
|
|
118
|
+
self._width = value
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def height(self):
|
|
122
|
+
"""int : The height of the texture buffer.
|
|
123
|
+
"""
|
|
124
|
+
return self._height
|
|
125
|
+
|
|
126
|
+
@height.setter
|
|
127
|
+
def height(self, value):
|
|
128
|
+
self._height = value
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def tex_type(self):
|
|
132
|
+
"""int : The type of the texture.
|
|
133
|
+
"""
|
|
134
|
+
return self._tex_type
|
|
135
|
+
|
|
136
|
+
@tex_type.setter
|
|
137
|
+
def tex_type(self, value):
|
|
138
|
+
self._tex_type = value
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def data_format(self):
|
|
142
|
+
"""int : The format of the texture data.
|
|
143
|
+
"""
|
|
144
|
+
return self._data_format
|
|
145
|
+
|
|
146
|
+
@data_format.setter
|
|
147
|
+
def data_format(self, value):
|
|
148
|
+
self._data_format = value
|
|
149
|
+
|
|
150
|
+
def is_transparent(self, cutoff=1.0):
|
|
151
|
+
"""bool : If True, the texture is partially transparent.
|
|
152
|
+
"""
|
|
153
|
+
if self._is_transparent is None:
|
|
154
|
+
self._is_transparent = False
|
|
155
|
+
if self.source_channels == 'RGBA' and self.source is not None:
|
|
156
|
+
if np.any(self.source[:,:,3] < cutoff):
|
|
157
|
+
self._is_transparent = True
|
|
158
|
+
return self._is_transparent
|
|
159
|
+
|
|
160
|
+
def delete(self):
|
|
161
|
+
"""Remove this texture from the OpenGL context.
|
|
162
|
+
"""
|
|
163
|
+
self._unbind()
|
|
164
|
+
self._remove_from_context()
|
|
165
|
+
|
|
166
|
+
##################
|
|
167
|
+
# OpenGL code
|
|
168
|
+
##################
|
|
169
|
+
def _add_to_context(self):
|
|
170
|
+
if self._texid is not None:
|
|
171
|
+
raise ValueError('Texture already loaded into OpenGL context')
|
|
172
|
+
|
|
173
|
+
fmt = GL_DEPTH_COMPONENT
|
|
174
|
+
if self.source_channels == 'R':
|
|
175
|
+
fmt = GL_RED
|
|
176
|
+
elif self.source_channels == 'RG' or self.source_channels == 'GB':
|
|
177
|
+
fmt = GL_RG
|
|
178
|
+
elif self.source_channels == 'RGB':
|
|
179
|
+
fmt = GL_RGB
|
|
180
|
+
elif self.source_channels == 'RGBA':
|
|
181
|
+
fmt = GL_RGBA
|
|
182
|
+
|
|
183
|
+
# Generate the OpenGL texture
|
|
184
|
+
self._texid = glGenTextures(1)
|
|
185
|
+
glBindTexture(self.tex_type, self._texid)
|
|
186
|
+
|
|
187
|
+
# Flip data for OpenGL buffer
|
|
188
|
+
data = None
|
|
189
|
+
width = self.width
|
|
190
|
+
height = self.height
|
|
191
|
+
if self.source is not None:
|
|
192
|
+
data = np.ascontiguousarray(np.flip(self.source, axis=0).flatten())
|
|
193
|
+
width = self.source.shape[1]
|
|
194
|
+
height = self.source.shape[0]
|
|
195
|
+
|
|
196
|
+
# Bind texture and generate mipmaps
|
|
197
|
+
glTexImage2D(
|
|
198
|
+
self.tex_type, 0, fmt, width, height, 0, fmt,
|
|
199
|
+
self.data_format, data
|
|
200
|
+
)
|
|
201
|
+
if self.source is not None:
|
|
202
|
+
glGenerateMipmap(self.tex_type)
|
|
203
|
+
|
|
204
|
+
if self.sampler.magFilter is not None:
|
|
205
|
+
glTexParameteri(
|
|
206
|
+
self.tex_type, GL_TEXTURE_MAG_FILTER, self.sampler.magFilter
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
if self.source is not None:
|
|
210
|
+
glTexParameteri(self.tex_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
|
211
|
+
else:
|
|
212
|
+
glTexParameteri(self.tex_type, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
|
|
213
|
+
if self.sampler.minFilter is not None:
|
|
214
|
+
glTexParameteri(
|
|
215
|
+
self.tex_type, GL_TEXTURE_MIN_FILTER, self.sampler.minFilter
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
if self.source is not None:
|
|
219
|
+
glTexParameteri(self.tex_type, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR)
|
|
220
|
+
else:
|
|
221
|
+
glTexParameteri(self.tex_type, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
|
|
222
|
+
|
|
223
|
+
glTexParameteri(self.tex_type, GL_TEXTURE_WRAP_S, self.sampler.wrapS)
|
|
224
|
+
glTexParameteri(self.tex_type, GL_TEXTURE_WRAP_T, self.sampler.wrapT)
|
|
225
|
+
border_color = 255 * np.ones(4).astype(np.uint8)
|
|
226
|
+
if self.data_format == GL_FLOAT:
|
|
227
|
+
border_color = np.ones(4).astype(np.float32)
|
|
228
|
+
glTexParameterfv(
|
|
229
|
+
self.tex_type, GL_TEXTURE_BORDER_COLOR,
|
|
230
|
+
border_color
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Unbind texture
|
|
234
|
+
glBindTexture(self.tex_type, 0)
|
|
235
|
+
|
|
236
|
+
def _remove_from_context(self):
|
|
237
|
+
if self._texid is not None:
|
|
238
|
+
# TODO OPENGL BUG?
|
|
239
|
+
# glDeleteTextures(1, [self._texid])
|
|
240
|
+
glDeleteTextures([self._texid])
|
|
241
|
+
self._texid = None
|
|
242
|
+
|
|
243
|
+
def _in_context(self):
|
|
244
|
+
return self._texid is not None
|
|
245
|
+
|
|
246
|
+
def _bind(self):
|
|
247
|
+
# TODO HANDLE INDEXING INTO OTHER UV's
|
|
248
|
+
glBindTexture(self.tex_type, self._texid)
|
|
249
|
+
|
|
250
|
+
def _unbind(self):
|
|
251
|
+
glBindTexture(self.tex_type, 0)
|
|
252
|
+
|
|
253
|
+
def _bind_as_depth_attachment(self):
|
|
254
|
+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
|
255
|
+
self.tex_type, self._texid, 0)
|
|
256
|
+
|
|
257
|
+
def _bind_as_color_attachment(self):
|
|
258
|
+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
259
|
+
self.tex_type, self._texid, 0)
|