OpenMotionLab/MotionGPT
118
1"""Textures, conforming to the glTF 2.0 standards as specified in2https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-texture3 4Author: Matthew Matl5"""6import numpy as np7 8from OpenGL.GL import *9 10from .utils import format_texture_source11from .sampler import Sampler12 13 14class Texture(object):15 """A texture and its sampler.16 17 Parameters18 ----------19 name : str, optional20 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 created25 empty and width and height must be specified.26 source_channels : str27 Either `D`, `R`, `RG`, `GB`, `RGB`, or `RGBA`. Indicates the28 channels to extract from `source`. Any missing channels will be filled29 with `1.0`.30 width : int, optional31 For empty textures, the width of the texture buffer.32 height : int, optional33 For empty textures, the height of the texture buffer.34 tex_type : int35 Either GL_TEXTURE_2D or GL_TEXTURE_CUBE.36 data_format : int37 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_channels50 self.name = name51 self.sampler = sampler52 self.source = source53 self.width = width54 self.height = height55 self.tex_type = tex_type56 self.data_format = data_format57 58 self._texid = None59 self._is_transparent = False60 61 @property62 def name(self):63 """str : The user-defined name of this object.64 """65 return self._name66 67 @name.setter68 def name(self, value):69 if value is not None:70 value = str(value)71 self._name = value72 73 @property74 def sampler(self):75 """:class:`Sampler` : The sampler used by this texture.76 """77 return self._sampler78 79 @sampler.setter80 def sampler(self, value):81 if value is None:82 value = Sampler()83 self._sampler = value84 85 @property86 def source(self):87 """(h,w,c) uint8 or float or :class:`PIL.Image.Image` : The image88 used in this texture.89 """90 return self._source91 92 @source.setter93 def source(self, value):94 if value is None:95 self._source = None96 else:97 self._source = format_texture_source(value, self.source_channels)98 self._is_transparent = False99 100 @property101 def source_channels(self):102 """str : The channels that were extracted from the original source.103 """104 return self._source_channels105 106 @source_channels.setter107 def source_channels(self, value):108 self._source_channels = value109 110 @property111 def width(self):112 """int : The width of the texture buffer.113 """114 return self._width115 116 @width.setter117 def width(self, value):118 self._width = value119 120 @property121 def height(self):122 """int : The height of the texture buffer.123 """124 return self._height125 126 @height.setter127 def height(self, value):128 self._height = value129 130 @property131 def tex_type(self):132 """int : The type of the texture.133 """134 return self._tex_type135 136 @tex_type.setter137 def tex_type(self, value):138 self._tex_type = value139 140 @property141 def data_format(self):142 """int : The format of the texture data.143 """144 return self._data_format145 146 @data_format.setter147 def data_format(self, value):148 self._data_format = value149 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 = False155 if self.source_channels == 'RGBA' and self.source is not None:156 if np.any(self.source[:,:,3] < cutoff):157 self._is_transparent = True158 return self._is_transparent159 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 code168 ##################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_COMPONENT174 if self.source_channels == 'R':175 fmt = GL_RED176 elif self.source_channels == 'RG' or self.source_channels == 'GB':177 fmt = GL_RG178 elif self.source_channels == 'RGB':179 fmt = GL_RGB180 elif self.source_channels == 'RGBA':181 fmt = GL_RGBA182 183 # Generate the OpenGL texture184 self._texid = glGenTextures(1)185 glBindTexture(self.tex_type, self._texid)186 187 # Flip data for OpenGL buffer188 data = None189 width = self.width190 height = self.height191 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 mipmaps197 glTexImage2D(198 self.tex_type, 0, fmt, width, height, 0, fmt,199 self.data_format, data200 )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.magFilter207 )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.minFilter216 )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_color231 )232 233 # Unbind texture234 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 = None242 243 def _in_context(self):244 return self._texid is not None245 246 def _bind(self):247 # TODO HANDLE INDEXING INTO OTHER UV's248 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)260 