hub/venv/lib/python3.7/site-packages/trimesh/scene/lighting.py

278 lines
8.8 KiB
Python

"""
lighting.py
--------------
Hold basic information about lights.
Forked from the light model in `pyrender`:
https://github.com/mmatl/pyrender
"""
import numpy as np
from .. import util
from .. import visual
from .. import transformations
# default light color
_DEFAULT_RGBA = np.array([60, 60, 60, 255], dtype=np.uint8)
class Light(util.ABC):
"""
Base class for all light objects.
Attributes
----------
name : str, optional
Name of the light.
color : (4,) uint8
RGBA 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).
radius : float
Cutoff distance at which light's intensity may be considered to
have reached zero. Supported only for point and spot lights
Must be > 0.0
If None, the radius is assumed to be infinite.
"""
def __init__(self,
name=None,
color=None,
intensity=None,
radius=None):
if name is None:
# if name is not passed, make it something unique
self.name = 'light_{}'.format(util.unique_id(6).upper())
else:
# otherwise assign it
self.name = name
self.color = color
self.intensity = intensity
self.radius = radius
@property
def color(self):
return self._color
@color.setter
def color(self, value):
if value is None:
self._color = _DEFAULT_RGBA
else:
value = visual.to_rgba(value)
if len(value.shape) == 2:
value = value[0]
if value.shape != (4,):
raise ValueError("couldn't convert color to RGBA!")
# uint8 RGB color
self._color = value
@property
def intensity(self):
return self._intensity
@intensity.setter
def intensity(self, value):
if value is not None:
self._intensity = float(value)
else:
self._intensity = 1.0
@property
def radius(self):
return self._radius
@radius.setter
def radius(self, value):
if value is None or value < 0.0:
self._radius = value
else:
self._radius = float(value)
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).
Attributes
----------
name : str, optional
Name of the light.
color : (4,) unit8
RGBA 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).
radius : float
Cutoff distance at which light's intensity may be considered to
have reached zero. Supported only for point and spot lights, must be > 0.
If None, the radius is assumed to be infinite.
"""
def __init__(self,
name=None,
color=None,
intensity=None,
radius=None):
super(DirectionalLight, self).__init__(
name=name,
color=color,
intensity=intensity,
radius=radius
)
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).
Attributes
----------
name : str, optional
Name of the light.
color : (4,) uint8
RGBA 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).
radius : float
Cutoff distance at which light's intensity may be considered to
have reached zero. Supported only for point and spot lights, must be > 0.
If None, the radius is assumed to be infinite.
"""
def __init__(self,
name=None,
color=None,
intensity=None,
radius=None):
super(PointLight, self).__init__(
name=name,
color=color,
intensity=intensity,
radius=radius
)
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.
Attributes
----------
name : str, optional
Name of the light.
color : (4,) uint8
RGBA 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).
radius : float
Cutoff distance at which light's intensity may be considered to
have reached zero. Supported only for point and spot lights, must be > 0.
If None, the radius 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`.
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`.
"""
def __init__(self,
name=None,
color=None,
intensity=None,
radius=None,
innerConeAngle=0.0,
outerConeAngle=np.pi / 4.0):
super(SpotLight, self).__init__(
name=name,
color=color,
intensity=intensity,
radius=radius
)
self.outerConeAngle = outerConeAngle
self.innerConeAngle = innerConeAngle
@property
def innerConeAngle(self):
return self._innerConeAngle
@innerConeAngle.setter
def innerConeAngle(self, value):
if value < 0.0 or value > self.outerConeAngle:
raise ValueError('Invalid value for inner cone angle')
self._innerConeAngle = float(value)
@property
def outerConeAngle(self):
return self._outerConeAngle
@outerConeAngle.setter
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 autolight(scene):
"""
Generate a list of lights for a scene that looks decent.
Parameters
--------------
scene : trimesh.Scene
Scene with geometry
Returns
--------------
lights : [Light]
List of light objects
transforms : (len(lights), 4, 4) float
Transformation matrices for light positions.
"""
# create two default point lights
lights = [PointLight(), PointLight()]
# create two translation matrices for bounds corners
transforms = [transformations.translation_matrix(b)
for b in scene.bounds]
return lights, transforms