Source code for arcade.gl.sampler

from __future__ import annotations

import weakref
from ctypes import byref, c_uint32
from typing import TYPE_CHECKING

from pyglet import gl

from .types import PyGLuint, compare_funcs

if TYPE_CHECKING:
    from arcade.gl import Context, Texture2D


[docs] class Sampler: """ OpenGL sampler object. When bound to a texture unit it overrides all the sampling parameters of the texture channel. """ def __init__( self, ctx: "Context", texture: Texture2D, *, filter: tuple[PyGLuint, PyGLuint] | None = None, wrap_x: PyGLuint | None = None, wrap_y: PyGLuint | None = None, ): self._ctx = ctx self._glo = -1 value = c_uint32() gl.glGenSamplers(1, byref(value)) self._glo = value.value self.texture = texture # Default filters for float and integer textures # Integer textures should have NEAREST interpolation # by default 3.3 core doesn't really support it consistently. if "f" in self.texture._dtype: self._filter = gl.GL_LINEAR, gl.GL_LINEAR else: self._filter = gl.GL_NEAREST, gl.GL_NEAREST self._wrap_x = gl.GL_REPEAT self._wrap_y = gl.GL_REPEAT self._anisotropy = 1.0 self._compare_func: str | None = None # Only set texture parameters on non-multisample textures if self.texture._samples == 0: self.filter = filter or self._filter self.wrap_x = wrap_x or self._wrap_x self.wrap_y = wrap_y or self._wrap_y if self._ctx.gc_mode == "auto": weakref.finalize(self, Sampler.delete_glo, self._glo) @property def glo(self) -> PyGLuint: """The OpenGL sampler id""" return self._glo
[docs] def use(self, unit: int): """ Bind the sampler to a texture unit """ gl.glBindSampler(unit, self._glo)
[docs] def clear(self, unit: int): """ Unbind the sampler from a texture unit """ gl.glBindSampler(unit, 0)
@property def filter(self) -> tuple[int, int]: """ Get or set the ``(min, mag)`` filter for this texture. These are rules for how a texture interpolates. The filter is specified for minification and magnification. Default value is ``LINEAR, LINEAR``. Can be set to ``NEAREST, NEAREST`` for pixelated graphics. When mipmapping is used the min filter needs to be one of the ``MIPMAP`` variants. Accepted values:: # Enums can be accessed on the context or arcade.gl NEAREST # Nearest pixel LINEAR # Linear interpolate NEAREST_MIPMAP_NEAREST # Minification filter for mipmaps LINEAR_MIPMAP_NEAREST # Minification filter for mipmaps NEAREST_MIPMAP_LINEAR # Minification filter for mipmaps LINEAR_MIPMAP_LINEAR # Minification filter for mipmaps Also see * https://www.khronos.org/opengl/wiki/Texture#Mip_maps * https://www.khronos.org/opengl/wiki/Sampler_Object#Filtering """ return self._filter @filter.setter def filter(self, value: tuple[int, int]): if not isinstance(value, tuple) or not len(value) == 2: raise ValueError("Texture filter must be a 2 component tuple (min, mag)") self._filter = value gl.glSamplerParameteri(self._glo, gl.GL_TEXTURE_MIN_FILTER, self._filter[0]) gl.glSamplerParameteri(self._glo, gl.GL_TEXTURE_MAG_FILTER, self._filter[1]) @property def wrap_x(self) -> int: """ Get or set the horizontal wrapping of the texture. This decides how textures are read when texture coordinates are outside the ``[0.0, 1.0]`` area. Default value is ``REPEAT``. Valid options are:: # Note: Enums can also be accessed in arcade.gl # Repeat pixels on the y axis texture.wrap_x = ctx.REPEAT # Repeat pixels on the y axis mirrored texture.wrap_x = ctx.MIRRORED_REPEAT # Repeat the edge pixels when reading outside the texture texture.wrap_x = ctx.CLAMP_TO_EDGE # Use the border color (black by default) when reading outside the texture texture.wrap_x = ctx.CLAMP_TO_BORDER """ return self._wrap_x @wrap_x.setter def wrap_x(self, value: int): self._wrap_x = value gl.glSamplerParameteri(self._glo, gl.GL_TEXTURE_WRAP_S, value) @property def wrap_y(self) -> int: """ Get or set the horizontal wrapping of the texture. This decides how textures are read when texture coordinates are outside the ``[0.0, 1.0]`` area. Default value is ``REPEAT``. Valid options are:: # Note: Enums can also be accessed in arcade.gl # Repeat pixels on the x axis texture.wrap_x = ctx.REPEAT # Repeat pixels on the x axis mirrored texture.wrap_x = ctx.MIRRORED_REPEAT # Repeat the edge pixels when reading outside the texture texture.wrap_x = ctx.CLAMP_TO_EDGE # Use the border color (black by default) when reading outside the texture texture.wrap_x = ctx.CLAMP_TO_BORDER """ return self._wrap_y @wrap_y.setter def wrap_y(self, value: int): self._wrap_y = value gl.glSamplerParameteri(self._glo, gl.GL_TEXTURE_WRAP_T, value) @property def anisotropy(self) -> float: """Get or set the anisotropy for this texture.""" return self._anisotropy @anisotropy.setter def anisotropy(self, value): self._anisotropy = max(1.0, min(value, self._ctx.info.MAX_TEXTURE_MAX_ANISOTROPY)) gl.glSamplerParameterf(self._glo, gl.GL_TEXTURE_MAX_ANISOTROPY, self._anisotropy) @property def compare_func(self) -> str | None: """ Get or set the compare function for a depth texture:: texture.compare_func = None # Disable depth comparison completely texture.compare_func = '<=' # GL_LEQUAL texture.compare_func = '<' # GL_LESS texture.compare_func = '>=' # GL_GEQUAL texture.compare_func = '>' # GL_GREATER texture.compare_func = '==' # GL_EQUAL texture.compare_func = '!=' # GL_NOTEQUAL texture.compare_func = '0' # GL_NEVER texture.compare_func = '1' # GL_ALWAYS """ return self._compare_func @compare_func.setter def compare_func(self, value: str | None): if not self.texture._depth: raise ValueError("Depth comparison function can only be set on depth textures") if not isinstance(value, str) and value is not None: raise ValueError(f"value must be as string: {self._compare_funcs.keys()}") func = compare_funcs.get(value, None) if func is None: raise ValueError(f"value must be as string: {compare_funcs.keys()}") self._compare_func = value if value is None: gl.glSamplerParameteri(self._glo, gl.GL_TEXTURE_COMPARE_MODE, gl.GL_NONE) else: gl.glSamplerParameteri( self._glo, gl.GL_TEXTURE_COMPARE_MODE, gl.GL_COMPARE_REF_TO_TEXTURE ) gl.glSamplerParameteri(self._glo, gl.GL_TEXTURE_COMPARE_FUNC, func)
[docs] @staticmethod def delete_glo(glo: int) -> None: """ Delete the OpenGL object """ gl.glDeleteSamplers(1, glo)