def test_bounding_box_8(self): bbox = BBox(Point(0, 0, 0), Point(2, 3, 2)) p, r = bbox.bounding_sphere() self.assertEqual(p, Point(1,1.5,1)) self.assertEqual(r, math.sqrt(1.5*1.5+1+1))
def test_bounding_box_7(self): bbox1 = BBox(Point(0, 0, 0), Point(2, 2, 2)) bbox2 = BBox(Point(2.5, 2.5, 2.5), Point(3, 3, 3)) self.assertEqual(bbox1.overlaps(bbox2), False) bbox3 = BBox(Point(-1, -1, -1), Point(0.5, 0.5, 0.5)) self.assertEqual(bbox1.overlaps(bbox3), True)
def test_bounding_box_9(self): bbox = BBox(Point(-1, -1, -1), Point(1, 1, 1)) ray1 = Ray(Point(10, 10, 10), Vector(-1, -1, -1)) intersect, hit0, hit1 = bbox.intersect_p(ray1) self.assertTrue(intersect) ray2 = Ray(Point(10, 10, 10), Vector(-1, 1, -1)) intersect, hit0, hit1 = bbox.intersect_p(ray2) self.assertFalse(intersect) ray3 = Ray(Point(0, 0, 10), Vector(0, 0, -1)) intersect, hit0, hit1 = bbox.intersect_p(ray3) self.assertTrue(intersect) self.assertEqual(hit0, 9.0) self.assertEqual(hit1, 11.0)
def test_bounding_box_1(self): # test default constructor b = BBox() self.assertEqual(b.p_min, Point(float('inf'), float('inf'), float('inf'))) self.assertEqual(b.p_max, Point(-float('inf'), -float('inf'), -float('inf')))
def test_bounding_box_3(self): # test constructor from two points p1 = self.get_random_point() p2 = self.get_random_point() b2 = BBox(p1, p2) for i in range(3): self.assertEqual(b2.p_min[i], min(p1[i], p2[i])) self.assertEqual(b2.p_max[i], max(p1[i], p2[i]))
def test_transform_bbox(self): box = BBox(Point(-1, -2, 0), Point(0, 3, -4)) box_transformed = translate(Point(10, 20, 30))(box) self.assertTrue(isinstance(box_transformed, BBox)) self.assertEqual(box_transformed.p_min, Point(9, 18, 26)) self.assertEqual(box_transformed.p_max, Point(10, 23, 30)) box_transformed2 = scale(2, 3, 4)(box) self.assertTrue(isinstance(box_transformed2, BBox)) self.assertEqual(box_transformed2.p_min, Point(-2, -6, -16)) self.assertEqual(box_transformed2.p_max, Point(0, 9, 0))
def test_bounding_box_4(self): # test copy constructor bbox = BBox(Point(5, 5, 5), Point(7, 7, 7)) bbox2 = BBox.from_bbox(bbox) self.assertEqual(bbox.p_min, bbox2.p_min) self.assertEqual(bbox.p_max, bbox2.p_max) p1 = Point(6, 5.5, 7) p2 = Point(6, 7.5, 7) p3 = Point(6, 6.5, 4.5) # test methods self.assertEqual(bbox.inside(p1), True) self.assertEqual(bbox.inside(p2), False) self.assertEqual(bbox.inside(p3), False) bbox.expand(1) self.assertEqual(bbox.inside(p1), True) self.assertEqual(bbox.inside(p2), True) self.assertEqual(bbox.inside(p3), True)
def motion_bounds(self, bbox, use_inverse): """.""" if (not self.actually_animated): return inverse(self.start_transform)(bbox) ret = BBox() n_steps = 128 for i in range(n_steps): t = Transform() time = lerp( float(i) / float(n_steps - 1), self.start_time, self.end_time) t = self.Interpolate(time) if (use_inverse): t = inverse(t) raise Exception("check_code_next_line") # ret = union(ret, t(b)) return ret
class GridAccel(Aggregate): """Class describing a GridAccel.""" def __init__(self, primitives, refine_immediately): """Default constructor for GridAccel.""" # initialize self.primitives with primitives for grid if refine_immediately: self.primitives = [] for primitive in primitives: primitive.fully_refine(self.primitives) else: self.primitives = list(primitives) # compute bounds and choose grid resolution self.bounds = BBox() for primitive in self.primitives: self.bounds = union(self.bounds, primitive.world_bound()) delta = self.bounds.p_max - self.bounds.p_min # find voxels_per_unit_dist for grid max_axis = self.bounds.maximum_extent() inv_max_width = 1.0 / delta[max_axis] cube_root = 3.0 * pow(len(self.primitives), 1.0 / 3.0) voxels_per_unit_dist = cube_root * inv_max_width self.n_voxels = [] for axis in range(3): self.n_voxels.append( clamp(round_to_int(delta[axis] * voxels_per_unit_dist), 1, 64)) # compute voxel widths and allocate voxels self.width = Vector() self.inv_width = Vector() for axis in range(3): self.width[axis] = delta[axis] / self.n_voxels[axis] if self.width[axis] == 0.0: self.inv_width[axis] = 0.0 else: self.inv_width[axis] = 1.0 / self.width[axis] nv = self.n_voxels[0] * self.n_voxels[1] * self.n_voxels[2] # array of voxels, initialized at None self.voxels = [None] * nv # add primitives to grid voxels for primitive in self.primitives: # find voxel extent of primitive primitive_bound = primitive.world_bound() v_min = [] v_max = [] for axis in range(3): v_min.append(self._pos_to_voxel(primitive_bound.p_min, axis)) v_max.append(self._pos_to_voxel(primitive_bound.p_max, axis)) # add primitive to overlapping voxels for z in range(v_min[2], v_max[2] + 1): for y in range(v_min[1], v_max[1] + 1): for x in range(v_min[0], v_max[0] + 1): index = self._offset(x, y, z) if self.voxels[index] is None: self.voxels[index] = Voxel(primitive) else: self.voxels[index].add_primitive(primitive) # create reader-writer mutex for grid self.rw_lock = DummyRWLock() def world_bound(self): """Return the bounding box in world space.""" return self.bounds def can_intersect(self): """Return True if the aggregate can intersect.""" return True def intersect(self, ray, intersection): """Compute the intersection with the primitives.""" # check ray against overall grid bounds if self.bounds.inside(ray(ray.mint)): ray_t = ray.mint else: intersected, t0, t1 = self.bounds.intersect_p(ray) if not intersected: self.rw_lock.release() return False ray_t = t0 grid_intersect = ray(ray_t) # set up 3D DDA (Digital Differential Analyzer) for ray pos = [] next_crossing_t = [] delta_t = [] step = [] out = [] for axis in range(3): # compute current voxel for axis pos.append(self._pos_to_voxel(grid_intersect, axis)) if ray.d[axis] == 0.0: next_crossing_t.append(float('inf')) delta_t.append(float('inf')) step.append(1) out.append(self.n_voxels[axis]) elif ray.d[axis] > 0.0: # handle ray with positive direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis]+1, axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(self.width[axis] / ray.d[axis]) step.append(1) out.append(self.n_voxels[axis]) else: # handle ray with negative direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis], axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(-self.width[axis] / ray.d[axis]) step.append(-1) out.append(-1) # acquire a READ lock self.rw_lock.acquire_read() # walk ray through voxel grid hit_something = False while (True): # check for intersection in current voxel and advance to next voxel = self.voxels[self._offset(pos[0], pos[1], pos[2])] if voxel is not None: hit_something |= voxel.intersect(ray, intersection, self.rw_lock) # advance to next voxel # find step_axis for stepping to next voxel # don't use shift comparisons as it's slower than branching # in python (see /timing/minimum.py) if next_crossing_t[0] < next_crossing_t[1]: if next_crossing_t[0] < next_crossing_t[2]: step_axis = 0 else: step_axis = 2 else: if next_crossing_t[1] < next_crossing_t[2]: step_axis = 1 else: step_axis = 2 if ray.maxt < next_crossing_t[step_axis]: break pos[step_axis] += step[step_axis] if pos[step_axis] == out[step_axis]: break next_crossing_t[step_axis] += delta_t[step_axis] # release lock self.rw_lock.release() return hit_something def intersect_p(self, ray): """Return True if the ray intersects any primitive.""" # check ray against overall grid bounds if self.bounds.inside(ray(ray.mint)): ray_t = ray.mint else: intersected, t0, t1 = self.bounds.intersect_p(ray) if not intersected: self.rw_lock.release() return False ray_t = t0 grid_intersect = ray(ray_t) # set up 3D DDA (Digital Differential Analyzer) for ray pos = [] next_crossing_t = [] delta_t = [] step = [] out = [] for axis in range(3): # compute current voxel for axis pos.append(self._pos_to_voxel(grid_intersect, axis)) if ray.d[axis] == 0.0: next_crossing_t.append(float('inf')) delta_t.append(float('inf')) step.append(1) out.append(self.n_voxels[axis]) elif ray.d[axis] > 0.0: # handle ray with positive direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis]+1, axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(self.width[axis] / ray.d[axis]) step.append(1) out.append(self.n_voxels[axis]) else: # handle ray with negative direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis], axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(-self.width[axis] / ray.d[axis]) step.append(-1) out.append(-1) # acquire a READ lock self.rw_lock.acquire_read() # walk grid for shadow ray while (True): # check for intersection in current voxel and advance to next voxel = self.voxels[self._offset(pos[0], pos[1], pos[2])] if voxel and voxel.intersect_p(ray, self.rw_lock): self.rw_lock.release() return True # advance to next voxel # find step_axis for stepping to next voxel # don't use shift comparisons as it's slower than branching # in python (see /timing/minimum.py) if next_crossing_t[0] < next_crossing_t[1]: if next_crossing_t[0] < next_crossing_t[2]: step_axis = 0 else: step_axis = 2 else: if next_crossing_t[1] < next_crossing_t[2]: step_axis = 1 else: step_axis = 2 if ray.maxt < next_crossing_t[step_axis]: break pos[step_axis] += step[step_axis] if pos[step_axis] == out[step_axis]: break next_crossing_t[step_axis] += delta_t[step_axis] # release lock self.rw_lock.release() return False def _pos_to_voxel(self, point, axis): """Convert a 1D position into a voxel index.""" v = int((point[axis] - self.bounds.p_min[axis]) * self.inv_width[axis]) return clamp(v, 0, self.n_voxels[axis] - 1) def _voxel_to_pos(self, index, axis): """Convert a voxel index to a 1D position.""" return self.bounds.p_min[axis] + index * self.width[axis] def _offset(self, x, y, z): """Compute a voxel position based on its x, y, z indexes.""" return z * self.n_voxels[0] * self.n_voxels[1] + y * self.n_voxels[ 0] + x
def test_bounding_box_2(self): # test constructor from one point p = self.get_random_point() b1 = BBox(p) self.assertEqual(b1.p_min, p) self.assertEqual(b1.p_min, b1.p_max)
def __init__(self, primitives, refine_immediately): """Default constructor for GridAccel.""" # initialize self.primitives with primitives for grid if refine_immediately: self.primitives = [] for primitive in primitives: primitive.fully_refine(self.primitives) else: self.primitives = list(primitives) # compute bounds and choose grid resolution self.bounds = BBox() for primitive in self.primitives: self.bounds = union(self.bounds, primitive.world_bound()) delta = self.bounds.p_max - self.bounds.p_min # find voxels_per_unit_dist for grid max_axis = self.bounds.maximum_extent() inv_max_width = 1.0 / delta[max_axis] cube_root = 3.0 * pow(len(self.primitives), 1.0 / 3.0) voxels_per_unit_dist = cube_root * inv_max_width self.n_voxels = [] for axis in range(3): self.n_voxels.append( clamp(round_to_int(delta[axis] * voxels_per_unit_dist), 1, 64)) # compute voxel widths and allocate voxels self.width = Vector() self.inv_width = Vector() for axis in range(3): self.width[axis] = delta[axis] / self.n_voxels[axis] if self.width[axis] == 0.0: self.inv_width[axis] = 0.0 else: self.inv_width[axis] = 1.0 / self.width[axis] nv = self.n_voxels[0] * self.n_voxels[1] * self.n_voxels[2] # array of voxels, initialized at None self.voxels = [None] * nv # add primitives to grid voxels for primitive in self.primitives: # find voxel extent of primitive primitive_bound = primitive.world_bound() v_min = [] v_max = [] for axis in range(3): v_min.append(self._pos_to_voxel(primitive_bound.p_min, axis)) v_max.append(self._pos_to_voxel(primitive_bound.p_max, axis)) # add primitive to overlapping voxels for z in range(v_min[2], v_max[2] + 1): for y in range(v_min[1], v_max[1] + 1): for x in range(v_min[0], v_max[0] + 1): index = self._offset(x, y, z) if self.voxels[index] is None: self.voxels[index] = Voxel(primitive) else: self.voxels[index].add_primitive(primitive) # create reader-writer mutex for grid self.rw_lock = DummyRWLock()
def test_bounding_box_8(self): bbox = BBox(Point(0, 0, 0), Point(2, 3, 2)) p, r = bbox.bounding_sphere() self.assertEqual(p, Point(1, 1.5, 1)) self.assertEqual(r, math.sqrt(1.5 * 1.5 + 1 + 1))
def test_bounding_box_6(self): bbox1 = BBox(Point(0, -2, 0), Point(1, -1, 1)) bbox2 = BBox(Point(-2, 0, -2), Point(-1, 1, -1)) bbox3 = union(bbox1, bbox2) self.assertEqual(bbox3.p_min, Point(-2, -2, -2)) self.assertEqual(bbox3.p_max, Point(1, 1, 1))
def test_bounding_box_5(self): bbox1 = BBox(Point(0, -2, 0), Point(1, -1, 1)) p1 = Point(-3, 3, 0.5) bbox2 = union(bbox1, p1) self.assertEqual(bbox2.p_min, Point(-3, -2, 0)) self.assertEqual(bbox2.p_max, Point(1, 3, 1))
def __call__(self, elt): """Overload the operator(). Supported operations: * Transform(Point) * Transform(Vector) * Transform(Normal) * Transform(Ray) * Transform(RayDifferential) * Transform(BBox) """ if isinstance(elt, Point): x = elt.x y = elt.y z = elt.z xp = self.m.m[0][0] * x + self.m.m[0][1] * y + self.m.m[0][ 2] * z + self.m.m[0][3] yp = self.m.m[1][0] * x + self.m.m[1][1] * y + self.m.m[1][ 2] * z + self.m.m[1][3] zp = self.m.m[2][0] * x + self.m.m[2][1] * y + self.m.m[2][ 2] * z + self.m.m[2][3] wp = self.m.m[3][0] * x + self.m.m[3][1] * y + self.m.m[3][ 2] * z + self.m.m[3][3] if wp == 1.0: return Point(xp, yp, zp) else: return Point(xp, yp, zp) / wp elif isinstance(elt, Vector): x = elt.x y = elt.y z = elt.z xp = self.m.m[0][0] * x + self.m.m[0][1] * y + self.m.m[0][2] * z yp = self.m.m[1][0] * x + self.m.m[1][1] * y + self.m.m[1][2] * z zp = self.m.m[2][0] * x + self.m.m[2][1] * y + self.m.m[2][2] * z return Vector(xp, yp, zp) elif isinstance(elt, Normal): x = elt.x y = elt.y z = elt.z return Normal( self.m_inv.m[0][0] * x + self.m_inv.m[1][0] * y + self.m_inv.m[2][0] * z, self.m_inv.m[0][1] * x + self.m_inv.m[1][1] * y + self.m_inv.m[2][1] * z, self.m_inv.m[0][2] * x + self.m_inv.m[1][2] * y + self.m_inv.m[2][2] * z) elif isinstance(elt, RayDifferential): ray = RayDifferential.from_ray_differential(elt) ray.o = self(ray.o) ray.d = self(ray.d) ray.rx_origin = self(ray.rx_origin) ray.ry_origin = self(ray.ry_origin) ray.rx_direction = self(ray.rx_direction) ray.ry_direction = self(ray.ry_direction) return ray elif isinstance(elt, Ray): ray = Ray.from_ray(elt) ray.o = self(ray.o) ray.d = self(ray.d) return ray elif isinstance(elt, BBox): ret = BBox(self(Point(elt.p_min.x, elt.p_min.y, elt.p_min.z))) ret = union(ret, self(Point(elt.p_max.x, elt.p_min.y, elt.p_min.z))) ret = union(ret, self(Point(elt.p_min.x, elt.p_max.y, elt.p_min.z))) ret = union(ret, self(Point(elt.p_min.x, elt.p_min.y, elt.p_max.z))) ret = union(ret, self(Point(elt.p_min.x, elt.p_max.y, elt.p_max.z))) ret = union(ret, self(Point(elt.p_max.x, elt.p_max.y, elt.p_min.z))) ret = union(ret, self(Point(elt.p_max.x, elt.p_min.y, elt.p_max.z))) ret = union(ret, self(Point(elt.p_max.x, elt.p_max.y, elt.p_max.z))) return ret
class GridAccel(Aggregate): """Class describing a GridAccel.""" def __init__(self, primitives, refine_immediately): """Default constructor for GridAccel.""" # initialize self.primitives with primitives for grid if refine_immediately: self.primitives = [] for primitive in primitives: primitive.fully_refine(self.primitives) else: self.primitives = list(primitives) # compute bounds and choose grid resolution self.bounds = BBox() for primitive in self.primitives: self.bounds = union(self.bounds, primitive.world_bound()) delta = self.bounds.p_max - self.bounds.p_min # find voxels_per_unit_dist for grid max_axis = self.bounds.maximum_extent() inv_max_width = 1.0 / delta[max_axis] cube_root = 3.0 * pow(len(self.primitives), 1.0/3.0) voxels_per_unit_dist = cube_root * inv_max_width self.n_voxels = [] for axis in range(3): self.n_voxels.append(clamp( round_to_int(delta[axis] * voxels_per_unit_dist), 1, 64)) # compute voxel widths and allocate voxels self.width = Vector() self.inv_width = Vector() for axis in range(3): self.width[axis] = delta[axis] / self.n_voxels[axis] if self.width[axis] == 0.0: self.inv_width[axis] = 0.0 else: self.inv_width[axis] = 1.0 / self.width[axis] nv = self.n_voxels[0] * self.n_voxels[1] * self.n_voxels[2] # array of voxels, initialized at None self.voxels = [None] * nv # add primitives to grid voxels for primitive in self.primitives: # find voxel extent of primitive primitive_bound = primitive.world_bound() v_min = [] v_max = [] for axis in range(3): v_min.append(self._pos_to_voxel(primitive_bound.p_min, axis)) v_max.append(self._pos_to_voxel(primitive_bound.p_max, axis)) # add primitive to overlapping voxels for z in range(v_min[2], v_max[2]+1): for y in range(v_min[1], v_max[1]+1): for x in range(v_min[0], v_max[0]+1): index = self._offset(x, y, z) if self.voxels[index] is None: self.voxels[index] = Voxel(primitive) else: self.voxels[index].add_primitive(primitive) # create reader-writer mutex for grid self.rw_lock = DummyRWLock() def world_bound(self): """Return the bounding box in world space.""" return self.bounds def can_intersect(self): """Return True if the aggregate can intersect.""" return True def intersect(self, ray, intersection): """Compute the intersection with the primitives.""" # check ray against overall grid bounds if self.bounds.inside(ray(ray.mint)): ray_t = ray.mint else: intersected, t0, t1 = self.bounds.intersect_p(ray) if not intersected: self.rw_lock.release() return False ray_t = t0 grid_intersect = ray(ray_t) # set up 3D DDA (Digital Differential Analyzer) for ray pos = [] next_crossing_t = [] delta_t = [] step = [] out = [] for axis in range(3): # compute current voxel for axis pos.append(self._pos_to_voxel(grid_intersect, axis)) if ray.d[axis] == 0.0: next_crossing_t.append(float('inf')) delta_t.append(float('inf')) step.append(1) out.append(self.n_voxels[axis]) elif ray.d[axis] > 0.0: # handle ray with positive direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis]+1, axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(self.width[axis] / ray.d[axis]) step.append(1) out.append(self.n_voxels[axis]) else: # handle ray with negative direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis], axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(-self.width[axis] / ray.d[axis]) step.append(-1) out.append(-1) # acquire a READ lock self.rw_lock.acquire_read() # walk ray through voxel grid hit_something = False while(True): # check for intersection in current voxel and advance to next voxel = self.voxels[self._offset(pos[0], pos[1], pos[2])] if voxel is not None: hit_something |= voxel.intersect(ray, intersection, self.rw_lock) # advance to next voxel # find step_axis for stepping to next voxel # don't use shift comparisons as it's slower than branching # in python (see /timing/minimum.py) if next_crossing_t[0] < next_crossing_t[1]: if next_crossing_t[0] < next_crossing_t[2]: step_axis = 0 else: step_axis = 2 else: if next_crossing_t[1] < next_crossing_t[2]: step_axis = 1 else: step_axis = 2 if ray.maxt < next_crossing_t[step_axis]: break pos[step_axis] += step[step_axis] if pos[step_axis] == out[step_axis]: break next_crossing_t[step_axis] += delta_t[step_axis] # release lock self.rw_lock.release() return hit_something def intersect_p(self, ray): """Return True if the ray intersects any primitive.""" # check ray against overall grid bounds if self.bounds.inside(ray(ray.mint)): ray_t = ray.mint else: intersected, t0, t1 = self.bounds.intersect_p(ray) if not intersected: self.rw_lock.release() return False ray_t = t0 grid_intersect = ray(ray_t) # set up 3D DDA (Digital Differential Analyzer) for ray pos = [] next_crossing_t = [] delta_t = [] step = [] out = [] for axis in range(3): # compute current voxel for axis pos.append(self._pos_to_voxel(grid_intersect, axis)) if ray.d[axis] == 0.0: next_crossing_t.append(float('inf')) delta_t.append(float('inf')) step.append(1) out.append(self.n_voxels[axis]) elif ray.d[axis] > 0.0: # handle ray with positive direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis]+1, axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(self.width[axis] / ray.d[axis]) step.append(1) out.append(self.n_voxels[axis]) else: # handle ray with negative direction for voxel stepping next_crossing_t.append( ray_t + (self._voxel_to_pos(pos[axis], axis) - \ grid_intersect[axis]) / ray.d[axis]) delta_t.append(-self.width[axis] / ray.d[axis]) step.append(-1) out.append(-1) # acquire a READ lock self.rw_lock.acquire_read() # walk grid for shadow ray while(True): # check for intersection in current voxel and advance to next voxel = self.voxels[self._offset(pos[0], pos[1], pos[2])] if voxel and voxel.intersect_p(ray, self.rw_lock): self.rw_lock.release() return True # advance to next voxel # find step_axis for stepping to next voxel # don't use shift comparisons as it's slower than branching # in python (see /timing/minimum.py) if next_crossing_t[0] < next_crossing_t[1]: if next_crossing_t[0] < next_crossing_t[2]: step_axis = 0 else: step_axis = 2 else: if next_crossing_t[1] < next_crossing_t[2]: step_axis = 1 else: step_axis = 2 if ray.maxt < next_crossing_t[step_axis]: break pos[step_axis] += step[step_axis] if pos[step_axis] == out[step_axis]: break next_crossing_t[step_axis] += delta_t[step_axis] # release lock self.rw_lock.release() return False def _pos_to_voxel(self, point, axis): """Convert a 1D position into a voxel index.""" v = int((point[axis] - self.bounds.p_min[axis]) * self.inv_width[axis]) return clamp(v, 0, self.n_voxels[axis]-1) def _voxel_to_pos(self, index, axis): """Convert a voxel index to a 1D position.""" return self.bounds.p_min[axis] + index * self.width[axis] def _offset(self, x, y, z): """Compute a voxel position based on its x, y, z indexes.""" return z*self.n_voxels[0]*self.n_voxels[1] + y*self.n_voxels[0] + x
def object_bound(self): """Return bounding box in object space.""" return BBox(Point(-self.radius, -self.radius, self.z_min), Point(self.radius, self.radius, self.z_max))
def __init__(self, primitives, refine_immediately): """Default constructor for GridAccel.""" # initialize self.primitives with primitives for grid if refine_immediately: self.primitives = [] for primitive in primitives: primitive.fully_refine(self.primitives) else: self.primitives = list(primitives) # compute bounds and choose grid resolution self.bounds = BBox() for primitive in self.primitives: self.bounds = union(self.bounds, primitive.world_bound()) delta = self.bounds.p_max - self.bounds.p_min # find voxels_per_unit_dist for grid max_axis = self.bounds.maximum_extent() inv_max_width = 1.0 / delta[max_axis] cube_root = 3.0 * pow(len(self.primitives), 1.0/3.0) voxels_per_unit_dist = cube_root * inv_max_width self.n_voxels = [] for axis in range(3): self.n_voxels.append(clamp( round_to_int(delta[axis] * voxels_per_unit_dist), 1, 64)) # compute voxel widths and allocate voxels self.width = Vector() self.inv_width = Vector() for axis in range(3): self.width[axis] = delta[axis] / self.n_voxels[axis] if self.width[axis] == 0.0: self.inv_width[axis] = 0.0 else: self.inv_width[axis] = 1.0 / self.width[axis] nv = self.n_voxels[0] * self.n_voxels[1] * self.n_voxels[2] # array of voxels, initialized at None self.voxels = [None] * nv # add primitives to grid voxels for primitive in self.primitives: # find voxel extent of primitive primitive_bound = primitive.world_bound() v_min = [] v_max = [] for axis in range(3): v_min.append(self._pos_to_voxel(primitive_bound.p_min, axis)) v_max.append(self._pos_to_voxel(primitive_bound.p_max, axis)) # add primitive to overlapping voxels for z in range(v_min[2], v_max[2]+1): for y in range(v_min[1], v_max[1]+1): for x in range(v_min[0], v_max[0]+1): index = self._offset(x, y, z) if self.voxels[index] is None: self.voxels[index] = Voxel(primitive) else: self.voxels[index].add_primitive(primitive) # create reader-writer mutex for grid self.rw_lock = DummyRWLock()