def generate_v( origin: Vec3, normal: Vec3, valid_side: float, start_db: float, dist_from_origin: float, ray_num, ) -> list: """ Generates rays in a hemisphere to represent reflected rays """ rnd = random.random() * ray_num points = [] offset = 2.0 / ray_num increment = math.pi * (3.0 - math.sqrt(5.0)) for i in range(ray_num): y = ((i * offset) - 1) + (offset / 2) r = math.sqrt(1 - pow(y, 2)) phi = ((i + rnd) % ray_num) * increment x = math.cos(phi) * r z = math.sin(phi) * r vec = Vec3(x, y, z).normalize() if np.sign(normal.dot(vec)) != np.sign(valid_side): # Ensure that the rays are inside the object vec = -vec points.append(Ray(origin, vec, dist_from_origin, start_db)) return points
def calc_normal(self): """ Calculate the face normal using the vertices """ normal = np.cross( self.vertices[1].vec - self.vertices[0].vec, self.vertices[2].vec - self.vertices[0].vec, ) return Vec3(*normal).normalize()
def calc_center(vertices, faces): """ Calculates center of the object denoted by the given vertices and faces using triangulation """ centers = [] for face in faces: centers.append( triangle_center(face.vertices[0], face.vertices[1], face.vertices[2]) ) return Vec3(*np.mean(centers, axis=0))
def is_inside(self, point: Vec3, v0: Vec3, v1: Vec3, v2: Vec3, normal: Vec3) -> bool: """ Determines if the point is inside the given triangle bounds and on the same plane """ edge0 = v1.sub(v0) edge1 = v2.sub(v1) edge2 = v0.sub(v2) C0 = point.sub(v0) C1 = point.sub(v1) C2 = point.sub(v2) if (normal.dot(edge0.cross(C0)) >= 0 and normal.dot(edge1.cross(C1)) >= 0 and normal.dot(edge2.cross(C2)) >= 0 and C0.dot(C1.cross(C2)) == 0.0): return True return False
def load_model(self, filename): """ Loads the specified file and generates the corresponding model. """ self.filename = filename self.obj_file = ObjLoader(filename) self.object_vertices = self.obj_file.vertices self.object_faces = self.obj_file.faces # Calculate object geometric properties self.object_volume = volume(self.object_vertices, self.object_faces) self.object_surface_area = surface_area(self.object_faces) self.object_center = calc_center(self.object_vertices, self.object_faces) self.x_pos = -self.object_center.x self.y_pos = -self.object_center.y self.zoom = -self.object_center.z * 10 self.sound_source = Vec3(*self.object_center) self.object = self.obj_file.render() self.update()
def __init__(self, parent=None, filename=""): super().__init__(parent) self.filename = filename self.object = None self.object_volume = 0 self.object_surface_area = 0 self.object_center = Vec3(0, 0, 0) self.raytracer = 0 self.rays = None self.x_rot = 0 self.y_rot = 0 self.z_rot = 0 self.x_pos = 0 self.y_pos = 0 self.zoom = -5.0 self.sound_source = None self.last_pos = QPoint() self.color_black = QColor.fromRgb(0, 0, 0) self.color_white = QColor.fromRgb(255, 255, 255)
def generate_rays(self) -> list: """ Generates an array of rays to use for the raytracing """ rnd = random.random() * self.ray_num points = [] offset = 2.0 / self.ray_num increment = math.pi * (3.0 - math.sqrt(5.0)) for i in range(self.ray_num): y = ((i * offset) - 1) + (offset / 2) r = math.sqrt(1 - pow(y, 2)) phi = ((i + rnd) % self.ray_num) * increment x = math.cos(phi) * r z = math.sin(phi) * r points.append( Ray(self.origin, Vec3(x, y, z).normalize(), 1, self.start_db)) return points
def signed_volume_triangle(v1: Vec3, v2: Vec3, v3: Vec3): """ Calculates the signed volume of a triangle through its relation to the origin as a tetrahedron """ return v1.dot(v2.cross(v3)) / 6.0
def calc_reflection(self, phit, normal, dist_from_origin, db) -> "Ray": """ Calculates the reflected Ray based on incidence and normal """ ray = Vec3(*(-(normal * (self.direction.dot(normal) * 2)).sub(self.direction))) return Ray(phit, ray, dist_from_origin, db)
from geometry.vec3 import Vec3 if __name__ == '__main__': v = Vec3(1, 0, 0) w = Vec3(0, 1, 0) print(Vec3.cross(v, w))
def intersect(self, ray: Ray, rNum=0) -> bool: """ Determines the intersection point of the given ray for the current model """ EPSILON = sys.float_info.epsilon for face in self.faces: # Origin in plane if self.is_inside(ray.origin, *face.vertices, face.normal): continue # Check if ray is parallel to plane pvec = ray.direction.cross(face.edge2) det = face.edge1.dot(pvec) if det > -EPSILON and det < EPSILON: continue inv_det = 1.0 / det tvec = ray.origin.sub(face.vertices[0]) u = tvec.dot(pvec) * inv_det if u < 0.0 or u > 1.0: continue qvec = tvec.cross(face.edge1) v = ray.direction.dot(qvec) * inv_det if v < 0.0 or u + v > 1.0: continue # Distance from ray origin t = face.edge2.dot(qvec) * inv_det # Ray moves away from the plane if t < EPSILON: continue # Intersection point phit_long = ray.origin.add(Vec3(*(ray.direction * t))) phit = Vec3(*np.around(phit_long.vec, decimals=2)) # Calcualate the dB level at the intersection new_dist_from_origin = ray.dist_from_origin + ray.origin.distance( phit) db_change = drop_off(ray.dist_from_origin, new_dist_from_origin) point_db = ray.start_db - db_change # Log Point curr_point_db = self.point_dict.get((phit.x, phit.y, phit.z)) if curr_point_db is not None: self.point_dict[(phit.x, phit.y, phit.z)] = sum_levels( [point_db, curr_point_db]) else: self.point_dict[(phit.x, phit.y, phit.z)] = point_db if rNum > 0: # Calculate the reflected rays reflected_db = point_db * ( 1 - mtl.absorption(face.material, self.freq)) reflections = generate_brdf(ray, phit, reflected_db, new_dist_from_origin, face) for ray in reflections: if ray.start_db > 0: self.intersect(ray, rNum - 1) return True return False
def set_sound_source(self, x, y, z): """ Sets the visual position of the sound source. """ self.sound_source = Vec3(x, y, z) self.update()
def __init__(self, fileName): self.vertices = np.empty((0), dtype=Vec3) self.normals = np.empty((0), dtype=Vec3) self.faces = np.empty((0), dtype=Face) # Parse file and construct list of vertices, faces, and face normals try: file = open(fileName) for line in file: if line.startswith("v "): line = line.strip().split() vertex = Vec3(line[1], line[2], line[3]) self.vertices = np.append(self.vertices, vertex) elif line.startswith("vn"): line = line.strip().split() normal = Vec3(line[1], line[2], line[3]) self.normals = np.append(self.normals, normal) elif line.startswith("f"): if "/" in line: line = line.strip().split() faceData = [ line[1].split("/"), line[2].split("/"), line[3].split("/"), ] vertRef = [ int(faceData[0][0]), int(faceData[1][0]), int(faceData[2][0]), ] vertices = np.array( [ self.vertices[vertRef[0] - 1], self.vertices[vertRef[1] - 1], self.vertices[vertRef[2] - 1], ] ) # Calculate face normal using vertex normals faceNorm = None if len(self.normals) > 0: faceNorm = ( np.sum( np.array( [ self.normals[int(faceData[0][2]) - 1].vec, self.normals[int(faceData[1][2]) - 1].vec, self.normals[int(faceData[2][2]) - 1].vec, ], dtype=float, ), axis=0, ) / 3 ) faceNorm = Vec3(*(-faceNorm)) else: line = line.strip().split() vertices = (int(line[1]), int(line[2]), int(line[3])) faceNorm = Vec3(0, 0, 0) self.faces = np.append(self.faces, Face(vertices, faceNorm)) file.close() except IOError: print(".obj file not found.")