def _create_axis_vectors() -> PointsCloud: points = np.array([ [0, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1], ], np.float32) colors = np.array([ [1, 1, 1, 1], [1, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 1], ], np.float32) edges = np.array([[0, 1], [0, 2], [0, 3]], np.int32) return PointsCloud(verts=points, colors=colors, edges=edges)
def bunny_test(self): bunny = ply.read_ply(test_support.resources_dir_path / 'scenes' / 'bunny' / 'bunny.ply') def transform_bunny(ps): world_pos = [0.5, -0.2, 0.0] ps[:, 0] += world_pos[0] ps[:, [0, 1, 2]] = ps[:, [0, 2, 1]] ps = world_pos + (ps - world_pos) * 4 return ps scene = Scene() scene.add_renderable( PointsCloud(transform_bunny(bunny['xyz']).copy(), faces=bunny['face'])) camera = Camera(course=-120) projector = StripesProjector() viewport = (5616, 3744) projector_lods = 10 self.reconstruction_case('bunny', scene, camera, projector, viewport, projector_lods)
if __name__ == '__main__': import sys from triangulum_test.test_support import resources_dir_path logging.basicConfig(level=logging.DEBUG, format='%(relativeCreated)d [%(threadName)s]\t%(name)s [%(levelname)s]:\t %(message)s') frame = Frame3D() datas = [] for points_path in sys.argv[1:]: points = points_cloud.load_ply(points_path) frame.add_points_cloud(points) if len(sys.argv[1:]) == 0: dog_mesh = PointsCloud(np.array([[1, -0.5, 0], [0, -0.5, 0], [0, -0.5, 1.5], [1, -0.5, 1.5]], np.float32), uv=np.float32(aabb.rect_to_quad([[0, 0], [1, 1]])), faces=np.int32([[0, 1, 2], [0, 2, 3]]), name='USSR Cute Dog Poster') box1 = Box(xs=[0.2, 0.4], ys=[0.0, -0.4], zs=[0.6, 0.8], course=-20, roll=45) box2 = Box(xs=[0.2, 0.5], ys=[0.0, -0.3], zs=[0.1, 0.4], course=15, pitch=10) texture = asyncio.get_event_loop().run_until_complete(frame._gl_executor.map(gl.create_image_texture, resources_dir_path / 'data' / 'dog.jpg')) bunny = ply.read_ply(resources_dir_path / 'scenes' / 'bunny' / 'bunny.ply') dog_mesh.set_texture(texture) def move_bunny(ps): world_pos = np.array([0.5, -0.2, 0.0]) ps[:, 0] += world_pos[0] ps[:, [0, 1, 2]] = ps[:, [0, 2, 1]] ps = world_pos + (ps - world_pos) * 7
def _update_frustum_mesh(self): # TODO: update on every camera movement # (currently this is not a problem, because only StripesProjector is rendered) self._frustum_mesh = PointsCloud(self.create_frustum_points(), faces=[[1, 2, 3], [1, 3, 4]], edges=[[0, i] for i in range(1, 5)])
class Camera(Renderable): def __init__(self, *, course=-135, pitch=360-30, distance=5, target=(0, 0, 0), aspect=1.0, fov_h=45, near=0.1, far=100000.0, mode: CameraMode=CameraMode.perspective): self.course = course """ Course in degrees, 0 - heading along the Y axis, clockwise rotation (looking down, like Gods!) """ self.pitch = pitch """ Pitch in degrees, 0 - heading horizontally (parallel XY plane, along course), (0 .. 180) - heading up, 180 - heading horizontally backwards, (180 .. 360) - looking down, 360-90 - looking along -Z axis """ self.max_pitch = 360-0 self.min_pitch = 360-90 assert self.min_pitch <= self.pitch <= self.max_pitch self.target = np.array(target, np.float32) self.distance = distance self.mode = mode self.aspect = aspect self.near = near self.far = far self.fov_h = fov_h self._frustum_mesh = None ''':type : triangulum.rendering.entities.points_cloud.PointsCloud''' self._projector = None self._update_frustum_mesh() def _update_frustum_mesh(self): # TODO: update on every camera movement # (currently this is not a problem, because only StripesProjector is rendered) self._frustum_mesh = PointsCloud(self.create_frustum_points(), faces=[[1, 2, 3], [1, 3, 4]], edges=[[0, i] for i in range(1, 5)]) def set_projector(self, projector): self._projector = projector self._frustum_mesh.set_projector(projector) def render(self, camera, *, edges_mode=False): self._frustum_mesh.render(camera, edges_mode=edges_mode) def _get_camera_direction(self): course = np.radians([self.course])[0] pitch = np.radians([self.pitch])[0] if pitch == 270: return np.array([0, 0, -1]) v = np.array([0, np.cos(pitch), np.sin(pitch)]) v = np.dot(math.rotate_matrix2d(-course), v) return v @staticmethod def _get_direction(course): course = np.radians(course) return np.array([np.sin(course), np.cos(course), 0]) def rotate(self, course, pitch): self.course = (self.course + course) % 360 self.pitch = self.pitch + pitch self.pitch = np.clip(self.pitch, self.min_pitch, self.max_pitch) def move(self, x, y): """ :param x: axis going to right from camera :param y: axis going with XY projection of camera direction """ direction = self._get_direction(self.course) right = self._get_direction(self.course + 90) x, y = (right[:2] * x + direction[:2] * y) * self.get_viewport_width() self.target[:2] += [x, y] def zoom_in(self, coef): """ :param coef: if positive - zoom in, if negative - zoom out, if zero - nothing changes """ self.distance /= 1.1 ** coef def set_aspect(self, aspect): self.aspect = aspect def get_mv_matrix(self): camera_position = self.target - math.normalize(self._get_camera_direction()) * self.distance return math.look_at_matrix(camera_position, self.target, up=[0, 0, 1], right=self._get_direction(self.course + 90)) def get_viewport_width(self): width = 2 * np.tan(np.radians([self.fov_h])[0] / 2) * self.distance return width def get_p_matrix(self): if self.mode is CameraMode.perspective: return math.perspective_matrix(self.aspect, self.near, self.far, self.fov_h) else: assert self.mode is CameraMode.ortho return math.ortho_matrix(self.aspect, self.near, self.far, self.get_viewport_width()) # noinspection PyPep8Naming def get_mvp_matrix(self): P = self.get_p_matrix() MV = self.get_mv_matrix() return np.dot(P, MV) def create_frustum_points(self, frustums_depth=1.0): rt_inv = np.linalg.inv(self.get_mv_matrix()) p = self.get_p_matrix()[:-1, :-1] camera_corners = math.homo_translate(np.linalg.inv(p), aabb.rect_to_quad([[-1.0, -1.0 * self.aspect], [1.0, 1.0 * self.aspect]])) corners = np.hstack([camera_corners, [[-1]] * 4]) * frustums_depth frustum_points = math.homo_translate(rt_inv, np.vstack([[[0, 0, 0]], corners])) return frustum_points
logging.basicConfig( level=logging.DEBUG, format= '%(relativeCreated)d [%(threadName)s]\t%(name)s [%(levelname)s]:\t %(message)s' ) frame = Frame3D() datas = [] for points_path in sys.argv[1:]: points = points_cloud.load_ply(points_path) frame.add_points_cloud(points) if len(sys.argv[1:]) == 0: dog_mesh = PointsCloud(np.array( [[1, -0.5, 0], [0, -0.5, 0], [0, -0.5, 1.5], [1, -0.5, 1.5]], np.float32), uv=np.float32( aabb.rect_to_quad([[0, 0], [1, 1]])), faces=np.int32([[0, 1, 2], [0, 2, 3]]), name='USSR Cute Dog Poster') box1 = Box(xs=[0.2, 0.4], ys=[0.0, -0.4], zs=[0.6, 0.8], course=-20, roll=45) box2 = Box(xs=[0.2, 0.5], ys=[0.0, -0.3], zs=[0.1, 0.4], course=15, pitch=10) texture = asyncio.get_event_loop().run_until_complete( frame._gl_executor.map(gl.create_image_texture,
class Camera(Renderable): def __init__(self, *, course=-135, pitch=360 - 30, distance=5, target=(0, 0, 0), aspect=1.0, fov_h=45, near=0.1, far=100000.0, mode: CameraMode = CameraMode.perspective): self.course = course """ Course in degrees, 0 - heading along the Y axis, clockwise rotation (looking down, like Gods!) """ self.pitch = pitch """ Pitch in degrees, 0 - heading horizontally (parallel XY plane, along course), (0 .. 180) - heading up, 180 - heading horizontally backwards, (180 .. 360) - looking down, 360-90 - looking along -Z axis """ self.max_pitch = 360 - 0 self.min_pitch = 360 - 90 assert self.min_pitch <= self.pitch <= self.max_pitch self.target = np.array(target, np.float32) self.distance = distance self.mode = mode self.aspect = aspect self.near = near self.far = far self.fov_h = fov_h self._frustum_mesh = None ''':type : triangulum.rendering.entities.points_cloud.PointsCloud''' self._projector = None self._update_frustum_mesh() def _update_frustum_mesh(self): # TODO: update on every camera movement # (currently this is not a problem, because only StripesProjector is rendered) self._frustum_mesh = PointsCloud(self.create_frustum_points(), faces=[[1, 2, 3], [1, 3, 4]], edges=[[0, i] for i in range(1, 5)]) def set_projector(self, projector): self._projector = projector self._frustum_mesh.set_projector(projector) def render(self, camera, *, edges_mode=False): self._frustum_mesh.render(camera, edges_mode=edges_mode) def _get_camera_direction(self): course = np.radians([self.course])[0] pitch = np.radians([self.pitch])[0] if pitch == 270: return np.array([0, 0, -1]) v = np.array([0, np.cos(pitch), np.sin(pitch)]) v = np.dot(math.rotate_matrix2d(-course), v) return v @staticmethod def _get_direction(course): course = np.radians(course) return np.array([np.sin(course), np.cos(course), 0]) def rotate(self, course, pitch): self.course = (self.course + course) % 360 self.pitch = self.pitch + pitch self.pitch = np.clip(self.pitch, self.min_pitch, self.max_pitch) def move(self, x, y): """ :param x: axis going to right from camera :param y: axis going with XY projection of camera direction """ direction = self._get_direction(self.course) right = self._get_direction(self.course + 90) x, y = (right[:2] * x + direction[:2] * y) * self.get_viewport_width() self.target[:2] += [x, y] def zoom_in(self, coef): """ :param coef: if positive - zoom in, if negative - zoom out, if zero - nothing changes """ self.distance /= 1.1**coef def set_aspect(self, aspect): self.aspect = aspect def get_mv_matrix(self): camera_position = self.target - math.normalize( self._get_camera_direction()) * self.distance return math.look_at_matrix(camera_position, self.target, up=[0, 0, 1], right=self._get_direction(self.course + 90)) def get_viewport_width(self): width = 2 * np.tan(np.radians([self.fov_h])[0] / 2) * self.distance return width def get_p_matrix(self): if self.mode is CameraMode.perspective: return math.perspective_matrix(self.aspect, self.near, self.far, self.fov_h) else: assert self.mode is CameraMode.ortho return math.ortho_matrix(self.aspect, self.near, self.far, self.get_viewport_width()) # noinspection PyPep8Naming def get_mvp_matrix(self): P = self.get_p_matrix() MV = self.get_mv_matrix() return np.dot(P, MV) def create_frustum_points(self, frustums_depth=1.0): rt_inv = np.linalg.inv(self.get_mv_matrix()) p = self.get_p_matrix()[:-1, :-1] camera_corners = math.homo_translate( np.linalg.inv(p), aabb.rect_to_quad([[-1.0, -1.0 * self.aspect], [1.0, 1.0 * self.aspect]])) corners = np.hstack([camera_corners, [[-1]] * 4]) * frustums_depth frustum_points = math.homo_translate(rt_inv, np.vstack([[[0, 0, 0]], corners])) return frustum_points