# MiniLight Python : minimal global illumination renderer # # Copyright (c) 2007-2008, Harrison Ainsworth / HXA7241 and Juraj Sukop. # http://www.hxa7241.org/ from triangle import Triangle, TOLERANCE from vector3f import Vector3f, MAX MAX_LEVELS = 44 MAX_ITEMS = 8 class SpatialIndex(object): def __init__(self, arg, items, level=0): if type(arg) == Vector3f: items = [(item.get_bound(), item) for item in items] bound = list(arg) * 2 for item in items: for j in range(6): if (bound[j] > item[0][j]) ^ (j > 2): bound[j] = item[0][j] size = max(list(Vector3f(bound[3:6]) - Vector3f(bound[0:3]))) self.bound = bound[0:3] + list(Vector3f(bound[3:6]).clamped(Vector3f(bound[0:3]) + Vector3f(size), MAX)) else: self.bound = arg self.is_branch = len(items) > MAX_ITEMS and level < MAX_LEVELS - 1 if self.is_branch: q1 = 0 self.vector = [None] * 8 for s in range(8): sub_bound = [] for j in range(6): m = j % 3 if (((s >> m) & 1) != 0) ^ (j > 2): sub_bound.append((self.bound[m] + self.bound[m + 3]) * 0.5) else: sub_bound.append(self.bound[j]) sub_items = [] for item in items: item_bound = item[0] if item_bound[3] >= sub_bound[0] and item_bound[0] < sub_bound[3] and \ item_bound[4] >= sub_bound[1] and item_bound[1] < sub_bound[4] and \ item_bound[5] >= sub_bound[2] and item_bound[2] < sub_bound[5]: sub_items.append(item) q1 += 1 if len(sub_items) == len(item) else 0 q2 = (sub_bound[3] - sub_bound[0]) < (TOLERANCE * 4.0) if len(sub_items) > 0: self.vector[s] = SpatialIndex(sub_bound, sub_items, MAX_LEVELS if q1 > 1 or q2 else level + 1) else: self.vector = [item[1] for item in items] def get_intersection(self, ray_origin, ray_direction, last_hit, start=None): start = start if start else ray_origin hit_object = hit_position = None if self.is_branch: sub_cell = 1 if start[0] >= (self.bound[0] + self.bound[3]) * 0.5 else 0 if start[1] >= (self.bound[1] + self.bound[4]) * 0.5: sub_cell |= 2 if start[2] >= (self.bound[2] + self.bound[5]) * 0.5: sub_cell |= 4 cell_position = start while True: if self.vector[sub_cell]: hit_object, hit_position = self.vector[sub_cell].get_intersection(ray_origin, ray_direction, last_hit, cell_position) if hit_object: break step = float(2**1024 - 2**971) axis = 0 for i in range(3): high = (sub_cell >> i) & 1 face = self.bound[i + high * 3] if (ray_direction[i] < 0.0) ^ (0 != high) else (self.bound[i] + self.bound[i + 3]) * 0.5 try: distance = (face - ray_origin[i]) / ray_direction[i] except: distance = float(1e30000) if distance <= step: step = distance axis = i if (((sub_cell >> axis) & 1) == 1) ^ (ray_direction[axis] < 0.0): break cell_position = ray_origin + ray_direction * step sub_cell = sub_cell ^ (1 << axis) else: nearest_distance = float(2**1024 - 2**971) for item in self.vector: if item != last_hit: distance = item.get_intersection(ray_origin, ray_direction) if distance and (distance < nearest_distance): hit = ray_origin + ray_direction * distance if (self.bound[0] - hit[0] <= TOLERANCE) and \ (hit[0] - self.bound[3] <= TOLERANCE) and \ (self.bound[1] - hit[1] <= TOLERANCE) and \ (hit[1] - self.bound[4] <= TOLERANCE) and \ (self.bound[2] - hit[2] <= TOLERANCE) and \ (hit[2] - self.bound[5] <= TOLERANCE): hit_object = item hit_position = hit nearest_distance = distance return hit_object, hit_position