Spaces:
Runtime error
Runtime error
| """Punctual light sources as defined by the glTF 2.0 KHR extension at | |
| https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual | |
| Author: Matthew Matl | |
| """ | |
| import abc | |
| import numpy as np | |
| import six | |
| from OpenGL.GL import * | |
| from .utils import format_color_vector | |
| from .texture import Texture | |
| from .constants import SHADOW_TEX_SZ | |
| from .camera import OrthographicCamera, PerspectiveCamera | |
| class Light(object): | |
| """Base class for all light objects. | |
| Parameters | |
| ---------- | |
| color : (3,) float | |
| RGB value for the light's color in linear space. | |
| intensity : float | |
| Brightness of light. The units that this is defined in depend on the | |
| type of light. Point and spot lights use luminous intensity in candela | |
| (lm/sr), while directional lights use illuminance in lux (lm/m2). | |
| name : str, optional | |
| Name of the light. | |
| """ | |
| def __init__(self, | |
| color=None, | |
| intensity=None, | |
| name=None): | |
| if color is None: | |
| color = np.ones(3) | |
| if intensity is None: | |
| intensity = 1.0 | |
| self.name = name | |
| self.color = color | |
| self.intensity = intensity | |
| self._shadow_camera = None | |
| self._shadow_texture = None | |
| def name(self): | |
| """str : The user-defined name of this object. | |
| """ | |
| return self._name | |
| def name(self, value): | |
| if value is not None: | |
| value = str(value) | |
| self._name = value | |
| def color(self): | |
| """(3,) float : The light's color. | |
| """ | |
| return self._color | |
| def color(self, value): | |
| self._color = format_color_vector(value, 3) | |
| def intensity(self): | |
| """float : The light's intensity in candela or lux. | |
| """ | |
| return self._intensity | |
| def intensity(self, value): | |
| self._intensity = float(value) | |
| def shadow_texture(self): | |
| """:class:`.Texture` : A texture used to hold shadow maps for this light. | |
| """ | |
| return self._shadow_texture | |
| def shadow_texture(self, value): | |
| if self._shadow_texture is not None: | |
| if self._shadow_texture._in_context(): | |
| self._shadow_texture.delete() | |
| self._shadow_texture = value | |
| def _generate_shadow_texture(self, size=None): | |
| """Generate a shadow texture for this light. | |
| Parameters | |
| ---------- | |
| size : int, optional | |
| Size of texture map. Must be a positive power of two. | |
| """ | |
| pass | |
| def _get_shadow_camera(self, scene_scale): | |
| """Generate and return a shadow mapping camera for this light. | |
| Parameters | |
| ---------- | |
| scene_scale : float | |
| Length of scene's bounding box diagonal. | |
| Returns | |
| ------- | |
| camera : :class:`.Camera` | |
| The camera used to render shadowmaps for this light. | |
| """ | |
| pass | |
| class DirectionalLight(Light): | |
| """Directional lights are light sources that act as though they are | |
| infinitely far away and emit light in the direction of the local -z axis. | |
| This light type inherits the orientation of the node that it belongs to; | |
| position and scale are ignored except for their effect on the inherited | |
| node orientation. Because it is at an infinite distance, the light is | |
| not attenuated. Its intensity is defined in lumens per metre squared, | |
| or lux (lm/m2). | |
| Parameters | |
| ---------- | |
| color : (3,) float, optional | |
| RGB value for the light's color in linear space. Defaults to white | |
| (i.e. [1.0, 1.0, 1.0]). | |
| intensity : float, optional | |
| Brightness of light, in lux (lm/m^2). Defaults to 1.0 | |
| name : str, optional | |
| Name of the light. | |
| """ | |
| def __init__(self, | |
| color=None, | |
| intensity=None, | |
| name=None): | |
| super(DirectionalLight, self).__init__( | |
| color=color, | |
| intensity=intensity, | |
| name=name, | |
| ) | |
| def _generate_shadow_texture(self, size=None): | |
| """Generate a shadow texture for this light. | |
| Parameters | |
| ---------- | |
| size : int, optional | |
| Size of texture map. Must be a positive power of two. | |
| """ | |
| if size is None: | |
| size = SHADOW_TEX_SZ | |
| self.shadow_texture = Texture(width=size, height=size, | |
| source_channels='D', data_format=GL_FLOAT) | |
| def _get_shadow_camera(self, scene_scale): | |
| """Generate and return a shadow mapping camera for this light. | |
| Parameters | |
| ---------- | |
| scene_scale : float | |
| Length of scene's bounding box diagonal. | |
| Returns | |
| ------- | |
| camera : :class:`.Camera` | |
| The camera used to render shadowmaps for this light. | |
| """ | |
| return OrthographicCamera( | |
| znear=0.01 * scene_scale, | |
| zfar=10 * scene_scale, | |
| xmag=scene_scale, | |
| ymag=scene_scale | |
| ) | |
| class PointLight(Light): | |
| """Point lights emit light in all directions from their position in space; | |
| rotation and scale are ignored except for their effect on the inherited | |
| node position. The brightness of the light attenuates in a physically | |
| correct manner as distance increases from the light's position (i.e. | |
| brightness goes like the inverse square of the distance). Point light | |
| intensity is defined in candela, which is lumens per square radian (lm/sr). | |
| Parameters | |
| ---------- | |
| color : (3,) float | |
| RGB value for the light's color in linear space. | |
| intensity : float | |
| Brightness of light in candela (lm/sr). | |
| range : float | |
| Cutoff distance at which light's intensity may be considered to | |
| have reached zero. If None, the range is assumed to be infinite. | |
| name : str, optional | |
| Name of the light. | |
| """ | |
| def __init__(self, | |
| color=None, | |
| intensity=None, | |
| range=None, | |
| name=None): | |
| super(PointLight, self).__init__( | |
| color=color, | |
| intensity=intensity, | |
| name=name, | |
| ) | |
| self.range = range | |
| def range(self): | |
| """float : The cutoff distance for the light. | |
| """ | |
| return self._range | |
| def range(self, value): | |
| if value is not None: | |
| value = float(value) | |
| if value <= 0: | |
| raise ValueError('Range must be > 0') | |
| self._range = value | |
| self._range = value | |
| def _generate_shadow_texture(self, size=None): | |
| """Generate a shadow texture for this light. | |
| Parameters | |
| ---------- | |
| size : int, optional | |
| Size of texture map. Must be a positive power of two. | |
| """ | |
| raise NotImplementedError('Shadows not implemented for point lights') | |
| def _get_shadow_camera(self, scene_scale): | |
| """Generate and return a shadow mapping camera for this light. | |
| Parameters | |
| ---------- | |
| scene_scale : float | |
| Length of scene's bounding box diagonal. | |
| Returns | |
| ------- | |
| camera : :class:`.Camera` | |
| The camera used to render shadowmaps for this light. | |
| """ | |
| raise NotImplementedError('Shadows not implemented for point lights') | |
| class SpotLight(Light): | |
| """Spot lights emit light in a cone in the direction of the local -z axis. | |
| The angle and falloff of the cone is defined using two numbers, the | |
| ``innerConeAngle`` and ``outerConeAngle``. | |
| As with point lights, the brightness | |
| also attenuates in a physically correct manner as distance increases from | |
| the light's position (i.e. brightness goes like the inverse square of the | |
| distance). Spot light intensity refers to the brightness inside the | |
| ``innerConeAngle`` (and at the location of the light) and is defined in | |
| candela, which is lumens per square radian (lm/sr). A spot light's position | |
| and orientation are inherited from its node transform. Inherited scale does | |
| not affect cone shape, and is ignored except for its effect on position | |
| and orientation. | |
| Parameters | |
| ---------- | |
| color : (3,) float | |
| RGB value for the light's color in linear space. | |
| intensity : float | |
| Brightness of light in candela (lm/sr). | |
| range : float | |
| Cutoff distance at which light's intensity may be considered to | |
| have reached zero. If None, the range is assumed to be infinite. | |
| innerConeAngle : float | |
| Angle, in radians, from centre of spotlight where falloff begins. | |
| Must be greater than or equal to ``0`` and less | |
| than ``outerConeAngle``. Defaults to ``0``. | |
| outerConeAngle : float | |
| Angle, in radians, from centre of spotlight where falloff ends. | |
| Must be greater than ``innerConeAngle`` and less than or equal to | |
| ``PI / 2.0``. Defaults to ``PI / 4.0``. | |
| name : str, optional | |
| Name of the light. | |
| """ | |
| def __init__(self, | |
| color=None, | |
| intensity=None, | |
| range=None, | |
| innerConeAngle=0.0, | |
| outerConeAngle=(np.pi / 4.0), | |
| name=None): | |
| super(SpotLight, self).__init__( | |
| name=name, | |
| color=color, | |
| intensity=intensity, | |
| ) | |
| self.outerConeAngle = outerConeAngle | |
| self.innerConeAngle = innerConeAngle | |
| self.range = range | |
| def innerConeAngle(self): | |
| """float : The inner cone angle in radians. | |
| """ | |
| return self._innerConeAngle | |
| def innerConeAngle(self, value): | |
| if value < 0.0 or value > self.outerConeAngle: | |
| raise ValueError('Invalid value for inner cone angle') | |
| self._innerConeAngle = float(value) | |
| def outerConeAngle(self): | |
| """float : The outer cone angle in radians. | |
| """ | |
| return self._outerConeAngle | |
| def outerConeAngle(self, value): | |
| if value < 0.0 or value > np.pi / 2.0 + 1e-9: | |
| raise ValueError('Invalid value for outer cone angle') | |
| self._outerConeAngle = float(value) | |
| def range(self): | |
| """float : The cutoff distance for the light. | |
| """ | |
| return self._range | |
| def range(self, value): | |
| if value is not None: | |
| value = float(value) | |
| if value <= 0: | |
| raise ValueError('Range must be > 0') | |
| self._range = value | |
| self._range = value | |
| def _generate_shadow_texture(self, size=None): | |
| """Generate a shadow texture for this light. | |
| Parameters | |
| ---------- | |
| size : int, optional | |
| Size of texture map. Must be a positive power of two. | |
| """ | |
| if size is None: | |
| size = SHADOW_TEX_SZ | |
| self.shadow_texture = Texture(width=size, height=size, | |
| source_channels='D', data_format=GL_FLOAT) | |
| def _get_shadow_camera(self, scene_scale): | |
| """Generate and return a shadow mapping camera for this light. | |
| Parameters | |
| ---------- | |
| scene_scale : float | |
| Length of scene's bounding box diagonal. | |
| Returns | |
| ------- | |
| camera : :class:`.Camera` | |
| The camera used to render shadowmaps for this light. | |
| """ | |
| return PerspectiveCamera( | |
| znear=0.01 * scene_scale, | |
| zfar=10 * scene_scale, | |
| yfov=np.clip(2 * self.outerConeAngle + np.pi / 16.0, 0.0, np.pi), | |
| aspectRatio=1.0 | |
| ) | |
| __all__ = ['Light', 'DirectionalLight', 'SpotLight', 'PointLight'] | |