Beispiel #1
0
 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))
Beispiel #2
0
    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)
Beispiel #3
0
    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)
Beispiel #4
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')))
Beispiel #5
0
    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)
Beispiel #6
0
 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]))
Beispiel #7
0
 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))
Beispiel #8
0
    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)
Beispiel #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)
Beispiel #10
0
 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
Beispiel #11
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)
Beispiel #12
0
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
Beispiel #13
0
 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)
Beispiel #14
0
    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()
Beispiel #15
0
    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))
Beispiel #16
0
 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))
Beispiel #17
0
 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))
Beispiel #18
0
    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
Beispiel #19
0
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
Beispiel #20
0
 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))
Beispiel #21
0
    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()