Spaces:
Runtime error
Runtime error
"""Virtual cameras compliant with the glTF 2.0 specification as described at | |
https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-camera | |
Author: Matthew Matl | |
""" | |
import abc | |
import numpy as np | |
import six | |
import sys | |
from .constants import DEFAULT_Z_NEAR, DEFAULT_Z_FAR | |
class Camera(object): | |
"""Abstract base class for all cameras. | |
Note | |
---- | |
Camera poses are specified in the OpenGL format, | |
where the z axis points away from the view direction and the | |
x and y axes point to the right and up in the image plane, respectively. | |
Parameters | |
---------- | |
znear : float | |
The floating-point distance to the near clipping plane. | |
zfar : float | |
The floating-point distance to the far clipping plane. | |
``zfar`` must be greater than ``znear``. | |
name : str, optional | |
The user-defined name of this object. | |
""" | |
def __init__(self, | |
znear=DEFAULT_Z_NEAR, | |
zfar=DEFAULT_Z_FAR, | |
name=None): | |
self.name = name | |
self.znear = znear | |
self.zfar = zfar | |
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 znear(self): | |
"""float : The distance to the near clipping plane. | |
""" | |
return self._znear | |
def znear(self, value): | |
value = float(value) | |
if value < 0: | |
raise ValueError('z-near must be >= 0.0') | |
self._znear = value | |
def zfar(self): | |
"""float : The distance to the far clipping plane. | |
""" | |
return self._zfar | |
def zfar(self, value): | |
value = float(value) | |
if value <= 0 or value <= self.znear: | |
raise ValueError('zfar must be >0 and >znear') | |
self._zfar = value | |
def get_projection_matrix(self, width=None, height=None): | |
"""Return the OpenGL projection matrix for this camera. | |
Parameters | |
---------- | |
width : int | |
Width of the current viewport, in pixels. | |
height : int | |
Height of the current viewport, in pixels. | |
""" | |
pass | |
class PerspectiveCamera(Camera): | |
"""A perspective camera for perspective projection. | |
Parameters | |
---------- | |
yfov : float | |
The floating-point vertical field of view in radians. | |
znear : float | |
The floating-point distance to the near clipping plane. | |
If not specified, defaults to 0.05. | |
zfar : float, optional | |
The floating-point distance to the far clipping plane. | |
``zfar`` must be greater than ``znear``. | |
If None, the camera uses an infinite projection matrix. | |
aspectRatio : float, optional | |
The floating-point aspect ratio of the field of view. | |
If not specified, the camera uses the viewport's aspect ratio. | |
name : str, optional | |
The user-defined name of this object. | |
""" | |
def __init__(self, | |
yfov, | |
znear=DEFAULT_Z_NEAR, | |
zfar=None, | |
aspectRatio=None, | |
name=None): | |
super(PerspectiveCamera, self).__init__( | |
znear=znear, | |
zfar=zfar, | |
name=name, | |
) | |
self.yfov = yfov | |
self.aspectRatio = aspectRatio | |
def yfov(self): | |
"""float : The vertical field of view in radians. | |
""" | |
return self._yfov | |
def yfov(self, value): | |
value = float(value) | |
if value <= 0.0: | |
raise ValueError('Field of view must be positive') | |
self._yfov = value | |
def zfar(self): | |
"""float : The distance to the far clipping plane. | |
""" | |
return self._zfar | |
def zfar(self, value): | |
if value is not None: | |
value = float(value) | |
if value <= 0 or value <= self.znear: | |
raise ValueError('zfar must be >0 and >znear') | |
self._zfar = value | |
def aspectRatio(self): | |
"""float : The ratio of the width to the height of the field of view. | |
""" | |
return self._aspectRatio | |
def aspectRatio(self, value): | |
if value is not None: | |
value = float(value) | |
if value <= 0.0: | |
raise ValueError('Aspect ratio must be positive') | |
self._aspectRatio = value | |
def get_projection_matrix(self, width=None, height=None): | |
"""Return the OpenGL projection matrix for this camera. | |
Parameters | |
---------- | |
width : int | |
Width of the current viewport, in pixels. | |
height : int | |
Height of the current viewport, in pixels. | |
""" | |
aspect_ratio = self.aspectRatio | |
if aspect_ratio is None: | |
if width is None or height is None: | |
raise ValueError('Aspect ratio of camera must be defined') | |
aspect_ratio = float(width) / float(height) | |
a = aspect_ratio | |
t = np.tan(self.yfov / 2.0) | |
n = self.znear | |
f = self.zfar | |
P = np.zeros((4,4)) | |
P[0][0] = 1.0 / (a * t) | |
P[1][1] = 1.0 / t | |
P[3][2] = -1.0 | |
if f is None: | |
P[2][2] = -1.0 | |
P[2][3] = -2.0 * n | |
else: | |
P[2][2] = (f + n) / (n - f) | |
P[2][3] = (2 * f * n) / (n - f) | |
return P | |
class OrthographicCamera(Camera): | |
"""An orthographic camera for orthographic projection. | |
Parameters | |
---------- | |
xmag : float | |
The floating-point horizontal magnification of the view. | |
ymag : float | |
The floating-point vertical magnification of the view. | |
znear : float | |
The floating-point distance to the near clipping plane. | |
If not specified, defaults to 0.05. | |
zfar : float | |
The floating-point distance to the far clipping plane. | |
``zfar`` must be greater than ``znear``. | |
If not specified, defaults to 100.0. | |
name : str, optional | |
The user-defined name of this object. | |
""" | |
def __init__(self, | |
xmag, | |
ymag, | |
znear=DEFAULT_Z_NEAR, | |
zfar=DEFAULT_Z_FAR, | |
name=None): | |
super(OrthographicCamera, self).__init__( | |
znear=znear, | |
zfar=zfar, | |
name=name, | |
) | |
self.xmag = xmag | |
self.ymag = ymag | |
def xmag(self): | |
"""float : The horizontal magnification of the view. | |
""" | |
return self._xmag | |
def xmag(self, value): | |
value = float(value) | |
if value <= 0.0: | |
raise ValueError('X magnification must be positive') | |
self._xmag = value | |
def ymag(self): | |
"""float : The vertical magnification of the view. | |
""" | |
return self._ymag | |
def ymag(self, value): | |
value = float(value) | |
if value <= 0.0: | |
raise ValueError('Y magnification must be positive') | |
self._ymag = value | |
def znear(self): | |
"""float : The distance to the near clipping plane. | |
""" | |
return self._znear | |
def znear(self, value): | |
value = float(value) | |
if value <= 0: | |
raise ValueError('z-near must be > 0.0') | |
self._znear = value | |
def get_projection_matrix(self, width=None, height=None): | |
"""Return the OpenGL projection matrix for this camera. | |
Parameters | |
---------- | |
width : int | |
Width of the current viewport, in pixels. | |
Unused in this function. | |
height : int | |
Height of the current viewport, in pixels. | |
Unused in this function. | |
""" | |
xmag = self.xmag | |
ymag = self.ymag | |
# If screen width/height defined, rescale xmag | |
if width is not None and height is not None: | |
xmag = width / height * ymag | |
n = self.znear | |
f = self.zfar | |
P = np.zeros((4,4)) | |
P[0][0] = 1.0 / xmag | |
P[1][1] = 1.0 / ymag | |
P[2][2] = 2.0 / (n - f) | |
P[2][3] = (f + n) / (n - f) | |
P[3][3] = 1.0 | |
return P | |
class IntrinsicsCamera(Camera): | |
"""A perspective camera with custom intrinsics. | |
Parameters | |
---------- | |
fx : float | |
X-axis focal length in pixels. | |
fy : float | |
Y-axis focal length in pixels. | |
cx : float | |
X-axis optical center in pixels. | |
cy : float | |
Y-axis optical center in pixels. | |
znear : float | |
The floating-point distance to the near clipping plane. | |
If not specified, defaults to 0.05. | |
zfar : float | |
The floating-point distance to the far clipping plane. | |
``zfar`` must be greater than ``znear``. | |
If not specified, defaults to 100.0. | |
name : str, optional | |
The user-defined name of this object. | |
""" | |
def __init__(self, | |
fx, | |
fy, | |
cx, | |
cy, | |
znear=DEFAULT_Z_NEAR, | |
zfar=DEFAULT_Z_FAR, | |
name=None): | |
super(IntrinsicsCamera, self).__init__( | |
znear=znear, | |
zfar=zfar, | |
name=name, | |
) | |
self.fx = fx | |
self.fy = fy | |
self.cx = cx | |
self.cy = cy | |
def fx(self): | |
"""float : X-axis focal length in meters. | |
""" | |
return self._fx | |
def fx(self, value): | |
self._fx = float(value) | |
def fy(self): | |
"""float : Y-axis focal length in meters. | |
""" | |
return self._fy | |
def fy(self, value): | |
self._fy = float(value) | |
def cx(self): | |
"""float : X-axis optical center in pixels. | |
""" | |
return self._cx | |
def cx(self, value): | |
self._cx = float(value) | |
def cy(self): | |
"""float : Y-axis optical center in pixels. | |
""" | |
return self._cy | |
def cy(self, value): | |
self._cy = float(value) | |
def get_projection_matrix(self, width, height): | |
"""Return the OpenGL projection matrix for this camera. | |
Parameters | |
---------- | |
width : int | |
Width of the current viewport, in pixels. | |
height : int | |
Height of the current viewport, in pixels. | |
""" | |
width = float(width) | |
height = float(height) | |
cx, cy = self.cx, self.cy | |
fx, fy = self.fx, self.fy | |
if sys.platform == 'darwin': | |
cx = self.cx * 2.0 | |
cy = self.cy * 2.0 | |
fx = self.fx * 2.0 | |
fy = self.fy * 2.0 | |
P = np.zeros((4,4)) | |
P[0][0] = 2.0 * fx / width | |
P[1][1] = 2.0 * fy / height | |
P[0][2] = 1.0 - 2.0 * cx / width | |
P[1][2] = 2.0 * cy / height - 1.0 | |
P[3][2] = -1.0 | |
n = self.znear | |
f = self.zfar | |
if f is None: | |
P[2][2] = -1.0 | |
P[2][3] = -2.0 * n | |
else: | |
P[2][2] = (f + n) / (n - f) | |
P[2][3] = (2 * f * n) / (n - f) | |
return P | |
__all__ = ['Camera', 'PerspectiveCamera', 'OrthographicCamera', | |
'IntrinsicsCamera'] | |