def ray_from_ndc(self, pos): """Retrieves a ray (origin, direction) corresponding to the given position on screen. This function takes the coordinates as NDC (normalized device coordinates), in which the lower-left corner of the screen corresponds to (-1,-1) and the upper-right corresponds to (1,1). For example, to convert mouse coordinates to NDC, you could do something like: >>> mouse_pos = pygame.mouse.get_pos() >>> mouse_pos = ((mouse_pos[0] / res_x) * 2 - 1, (mouse_pos[1] / res_y) * 2 - 1) >>> origin, dir = camera.RayFromNDC(mouse_pos) Arguments: pos {2-tuple} -- Screen position in NDC (normalized device coordinates) Returns: Vector3, Vector3 - Origin and direction of the ray corresponding to that screen positions """ vpos = Vector3(pos[0], pos[1], self.near_plane) vpos.x = vpos.x * self.res_x * 0.5 vpos.y = -vpos.y * self.res_y * 0.5 inv_view_proj_matrix = self.get_camera_matrix( ) @ self.get_projection_matrix() inv_view_proj_matrix = np.linalg.inv(inv_view_proj_matrix) direction = inv_view_proj_matrix @ vpos.to_np4(1) direction = Vector3.from_np(direction).normalized() return self.position + direction * self.near_plane, direction
def right(self): """ Retrieves the local right vector of this object. The right vector is defined as being the x-positive vector multiplied with the local transformation matrix Returns: {Vector3} - Local right vector of the object """ return Vector3.from_np(Vector3(1, 0, 0).to_np4(0) @ self.get_matrix())
def forward(self): """ Retrieves the local forward vector of this object. The forward vector is defined as being the z-positive vector multiplied with the local transformation matrix Returns: {Vector3} - Local forward vector of the object """ return Vector3.from_np(Vector3(0, 0, 1).to_np4(0) @ self.get_matrix())
def get_position(self): """ Retrieves the local position of this object. You can use self.position instead, this method actually computes the transfomation matrix and multiplies the 4d vector (0,0,0,1) by it. Results should be very similar. Returns: {Vector3} - Local position of the object """ return Vector3.from_np(Vector3(0, 0, 0).to_np4(1) @ self.get_matrix())
def __init__(self, name): """ Arguments: name {str} -- Name of the object """ self.name = name """ {str} Name of the object""" self.position = Vector3() """ {Vector3} Local position of the object (relative to parent)""" self.rotation = quaternion(1, 0, 0, 0) """ {quaternion} Local rotation of the object {relative to parent)""" self.scale = Vector3(1, 1, 1) """ {Vector3} Local scale of the object (relative to parent)""" self.mesh = None """ {Mesh} Mesh to be rendered in this object""" self.material = None """ {Material} Material to be used rendering this object""" self.children = [] """ {List[Object3d]} Children objects of this object"""
def create_cube(size, mesh=None): """ Adds the 6 polygons necessary to form a cube with the given size. If a source mesh is not given, a new mesh is created. This cube will be centered on the origin (0,0,0). Arguments: size {3-tuple} -- (x,y,z) size of the cube mesh {Mesh} -- Mesh to add the polygons. If not given, create a new mesh Returns: {Mesh} - Mesh where the polygons were added """ # Create mesh if one was not given if mesh is None: mesh = Mesh("UnknownCube") # Add the 6 quads that create a cube Mesh.create_quad(Vector3(size[0] * 0.5, 0, 0), Vector3(0, -size[1] * 0.5, 0), Vector3(0, 0, size[2] * 0.5), mesh) Mesh.create_quad(Vector3(-size[0] * 0.5, 0, 0), Vector3(0, size[1] * 0.5, 0), Vector3(0, 0, size[2] * 0.5), mesh) Mesh.create_quad(Vector3(0, size[1] * 0.5, 0), Vector3(size[0] * 0.5, 0), Vector3(0, 0, size[2] * 0.5), mesh) Mesh.create_quad(Vector3(0, -size[1] * 0.5, 0), Vector3(-size[0] * 0.5, 0), Vector3(0, 0, size[2] * 0.5), mesh) Mesh.create_quad(Vector3(0, 0, size[2] * 0.5), Vector3(-size[0] * 0.5, 0), Vector3(0, size[1] * 0.5, 0), mesh) Mesh.create_quad(Vector3(0, 0, -size[2] * 0.5), Vector3(size[0] * 0.5, 0), Vector3(0, size[1] * 0.5, 0), mesh) return mesh
def create_sphere(size, res_lat, res_lon, mesh=None): """ Adds the polygons necessary to form a sphere with the given size and resolution. If a source mesh is not given, a new mesh is created. This sphere will be centered on the origin (0,0,0). Arguments: size {3-tuple} -- (x,y,z) size of the sphere or size {number} -- radius of the sphere res_lat {int} -- Number of subdivisions in the latitude axis res_lon {int} -- Number of subdivisions in the longitudinal axis mesh {Mesh} -- Mesh to add the polygons. If not given, create a new mesh Returns: {Mesh} - Mesh where the polygons were added """ # Create mesh if one was not given if mesh is None: mesh = Mesh("UnknownSphere") # Compute half-size if isinstance(size, Vector3): hs = size * 0.5 else: hs = Vector3(size[0], size[1], size[2]) * 0.5 # Sphere is going to be composed by quads in most of the surface, but triangles near the # poles, so compute the bottom and top vertex bottom_vertex = Vector3(0, -hs.y, 0) top_vertex = Vector3(0, hs.y, 0) lat_inc = math.pi / res_lat lon_inc = math.pi * 2 / res_lon # First row of triangles lat = -math.pi / 2 lon = 0 y = hs.y * math.sin(lat + lat_inc) c = math.cos(lat + lat_inc) for _ in range(0, res_lon): p1 = Vector3(c * math.cos(lon) * hs.x, y, c * math.sin(lon) * hs.z) p2 = Vector3(c * math.cos(lon + lon_inc) * hs.x, y, c * math.sin(lon + lon_inc) * hs.z) Mesh.create_tri(bottom_vertex, p1, p2, mesh) lon += lon_inc # Quads in the middle for _ in range(1, res_lat - 1): lat += lat_inc y1 = hs.y * math.sin(lat) y2 = hs.y * math.sin(lat + lat_inc) c1 = math.cos(lat) c2 = math.cos(lat + lat_inc) lon = 0 for _ in range(0, res_lon): p1 = Vector3(c1 * math.cos(lon) * hs.x, y1, c1 * math.sin(lon) * hs.z) p2 = Vector3(c1 * math.cos(lon + lon_inc) * hs.x, y1, c1 * math.sin(lon + lon_inc) * hs.z) p3 = Vector3(c2 * math.cos(lon) * hs.x, y2, c2 * math.sin(lon) * hs.z) p4 = Vector3(c2 * math.cos(lon + lon_inc) * hs.x, y2, c2 * math.sin(lon + lon_inc) * hs.z) poly = [] poly.append(p1) poly.append(p2) poly.append(p4) poly.append(p3) mesh.polygons.append(poly) lon += lon_inc # Last row of triangles lat += lat_inc y = hs.y * math.sin(lat) c = math.cos(lat) for _ in range(0, res_lon): p1 = Vector3(c * math.cos(lon) * hs.x, y, c * math.sin(lon) * hs.z) p2 = Vector3(c * math.cos(lon + lon_inc) * hs.x, y, c * math.sin(lon + lon_inc) * hs.z) Mesh.create_tri(top_vertex, p1, p2, mesh) lon += lon_inc return mesh