def main(): """Main function.""" if len(sys.argv) < 5: print "Error. Specify filename, sene, width, height." return -1 filename = sys.argv[1] scene = sys.argv[2] width = int(sys.argv[3]) height = int(sys.argv[4]) if scene == "sp": primitives = create_simple_sphere() cam_pos = Point(0, 3, 8) cam_look = Point(0, 0.8, 0) cam_up = Vector(0, 1, 0) elif scene == "py": primitives = create_pyramid() cam_pos = Point(10, 10, 4) cam_look = Point(0, 0, 2) cam_up = Vector(0, 0, 1) else: print "unknown scene" return -1 cam_transform = look_at(cam_pos, cam_look, cam_up) scene = create_scene(primitives) film = create_film(filename, width, height) camera = create_camera(film, cam_transform) renderer = create_renderer(camera) # launch render renderer.render(scene) return 0
def test_intersect(self): # test an intersection ray = Ray(Point(0, 0, 10), Vector(0, 0, -1)) intersection = Intersection() intersected = self.grid_accel.intersect(ray, intersection) self.assertFalse(intersected) ray.maxt = float('inf') intersected = self.grid_accel.intersect_p(ray) self.assertFalse(intersected) # test an intersection ray2 = Ray(Point(10, 0, 10), Vector(0, 0, -1)) intersection = Intersection() intersected = self.grid_accel.intersect(ray2, intersection) self.assertTrue(intersected) self.assertEqual(intersection.primitive_id, self.primitive_sphere1.primitive_id) self.assertEqual(intersection.shape_id, self.sphere1.shape_id) ray2.maxt = float('inf') intersected = self.grid_accel.intersect_p(ray2) self.assertTrue(intersected) # test an intersection ray3 = Ray(Point(-10, 0, 10), Vector(0, 0, -1)) intersection = Intersection() intersected = self.grid_accel.intersect(ray3, intersection) self.assertTrue(intersected) self.assertEqual(intersection.primitive_id, self.primitive_sphere2.primitive_id) self.assertEqual(intersection.shape_id, self.sphere2.shape_id) ray3.maxt = float('inf') intersected = self.grid_accel.intersect_p(ray3) self.assertTrue(intersected)
def test_dot_w_zero(self): v1 = Vector(1.0, 3.0, 5.0) v2 = Vector(2.0, 3.0, 4.0) q1 = Quaternion(v1, 0.0) q2 = Quaternion(v2, 0.0) scalar = dot_quaternions(q1, q2) self.assertEqual(scalar, dot(v1, v2))
def test_dot_perpendicular(self): v1 = Vector(1.0, 2.0, 0.0) v2 = Vector(0.0, 0.0, 5.0) q1 = Quaternion(v1, 3.0) q2 = Quaternion(v2, 4.0) scalar = dot_quaternions(q1, q2) self.assertEqual(scalar, 3.0*4.0)
def test_ray_differential(self): r = Ray(Point(0, 0, 0), Vector(1, 2, 3)) rd = RayDifferential(Point(0, 0, 0), Vector(1, 2, 3)) # test copy constructor from Ray rd.has_differentials = True rd1 = RayDifferential.from_ray_differential(rd) self.assertTrue(isinstance(rd1, RayDifferential)) self.assertEqual(rd1.o, rd.o) self.assertEqual(rd1.d, rd.d) self.assertEqual(rd1.rx_origin, rd.rx_origin) self.assertEqual(rd1.ry_origin, rd.ry_origin) self.assertEqual(rd1.rx_direction, rd.rx_direction) self.assertEqual(rd1.ry_direction, rd.ry_direction) self.assertEqual(rd1.has_differentials, rd.has_differentials) # test copy constructor from Ray rd2 = RayDifferential.from_ray(r) self.assertTrue(isinstance(rd2, RayDifferential)) self.assertEqual(rd2.d, r.d) self.assertEqual(rd2.has_differentials, False) # test constructor from parent ray rd3 = RayDifferential.from_ray_parent(r.o, r.d, r, r.mint) self.assertTrue(isinstance(rd3, RayDifferential)) self.assertEqual(rd3.depth, r.depth + 1) # test operator() p = rd(1.7) self.assertEqual(p, Point(1.7, 3.4, 5.1))
def create_pyramid(): primitives = [] # create the sphere primitive material = None object_to_world = Transform() world_to_object = Transform() sphere = Sphere(object_to_world, world_to_object, False, 1.0, -1.0, 1.0, 360) primitive = GeometricPrimitive(sphere, material, None) # now create the instances # create the objects, with this layout (shown in 2D) # O O O O level 0, width 4, pos_x -6 # O O O level 1, width 3, pos_x -4 # O O level 2, width 2, pos_x -2 # O level 3, width 1, pos_x 0 for level in range(4): width_array = 4 - level start_pos = Point(-2.0 * (3 - level), -2.0 * (3 - level), level * 2.0) for i in range(width_array): start_pos_i = start_pos + i * Vector(2.0, 0.0, 0.0) for j in range(width_array): pos = start_pos_i + j * Vector(0.0, 2.0, 0.0) transform = translate(pos).inverse() world_to_primitive = AnimatedTransform(transform, 0.0, transform, 1.0) instance = TransformedPrimitive(primitive, world_to_primitive) primitives.append(instance) return primitives
def has_scale(self): """Return True if the transform has a scaling term.""" la2 = self(Vector(1, 0, 0)).length_squared() lb2 = self(Vector(0, 1, 0)).length_squared() lc2 = self(Vector(0, 0, 1)).length_squared() not_one = lambda x: x < 0.999 or x > 1.001 return not_one(la2) or not_one(lb2) or not_one(lc2)
def test_rotate(self): self.assertEqual(rotate(40, Vector(1.0, 0.0, 0.0)), rotate_x(40)) self.assertEqual(rotate(20, Vector(0.0, 1.0, 0.0)), rotate_y(20)) self.assertEqual(rotate(70, Vector(0.0, 0.0, 1.0)), rotate_z(70))
def test_transform_ray(self): ray = Ray(origin=Point(1, 2, 3), direction=Vector(10, 20, 30)) ray_transformed = translate(Point(10, 20, 30))(ray) self.assertTrue(isinstance(ray_transformed, Ray)) self.assertEqual(ray_transformed.o, Point(11, 22, 33)) self.assertEqual(ray_transformed.d, Vector(10, 20, 30))
def compute_differentials(self, ray_diff): """Computes the differentials from a RayDifferential.""" if ray_diff.has_differentials: pass self.du_dx = 0.0 self.dv_dx = 0.0 self.du_dy = 0.0 self.dv_dy = 0.0 self.dp_dx = Vector(0, 0, 0) self.dp_dy = Vector(0, 0, 0)
def test_decompose(self): vector_translate = Vector(10.0, 20.0, 30.0) matrix_translate = translate(vector_translate) matrix_rotation = rotate(35.0, Vector(1.0, 2.0, 3.0)) vector_scale = Vector(1.2, 3.4, 3.2) matrix_scale = scale(vector_scale.x, vector_scale.y, vector_scale.z) transform = matrix_translate * matrix_rotation * matrix_scale vector_, quaternion_, scale_ = decompose(transform.m) self.assertEqual(vector_translate, vector_) self.assertEqual(matrix_rotation, quaternion_.to_transform()) self.assertEqual(matrix_scale.m, scale_)
def time_sphere_intersection(): # create a transform o2w = translate(Vector(10,0,0)) * scale(1.3, 1.8, 2.0) w2o = o2w.inverse() # create the sphere sphere = Sphere(o2w, w2o, False, 1.0, -1.0, 1.0, 360) # create a large amount of rays, # choose so that half of them will intersect the ray positions = [Point(random.randint(0,100), random.randint(0,100), random.randint(0,100) ) for i in range(size)] ray = Ray(Point(0,0,0), Vector(1.0, 1.0, 1.0)) vectors = [] for i in xrange(size): position = positions[i] if i%2 == 0: # make sure this ray hit the sphere vector = sphere.object_to_world(Point(0, 0, 0)) - position vector /= float(random.randint(1,10)) else: # construct a random vector vector = Vector((random.random()-0.5)*random.randint(1,5), (random.random()-0.5)*random.randint(1,5), (random.random()-0.5*random.randint(1,5))) vectors.append(vector) intersections = 0 t1 = time.time() for i in xrange(nb_calls): ray.o = positions[i%size] ray.d = vectors[i%size] if sphere.intersect_p(ray): intersections += 1 t2 = time.time() for i in xrange(nb_calls): ray.o = positions[i%size] ray.d = vectors[i%size] sphere.intersect(ray) t3 = time.time() print "%d calls, %d intersections" % (nb_calls, intersections) print "Sphere.intersect_p() %.2fms" % ((t2-t1)/float(nb_calls)*1000.0) print "Sphere.intersect() %.2fms" % ((t3-t2)/float(nb_calls)*1000.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)
def setUp(self): # create a transform o2w = translate(Vector(10, 0, 0)) * scale(1.3, 1.8, 2.0) w2o = o2w.inverse() # create the sphere self.sphere = Sphere(o2w, w2o, False, 1.0, -1.0, 1.0, 360)
def __init__(self, v=None, w=1.0): """Default constructor for Quaternion.""" if v is None: self.v = Vector(0.0, 0.0, 0.0) else: self.v = Vector.from_vector(v) self.w = w
def test_transform(self): p = Point(1, 2, 3) p2 = translate(Point(10, 20, 30))(p) self.assertTrue(isinstance(p2, Point)) self.assertEqual(p2, Point(11, 22, 33)) v = Vector(1, 2, 3) v2 = translate(Point(10, 20, 30))(v) self.assertTrue(isinstance(v2, Vector)) self.assertEqual(v2, Vector(1, 2, 3)) self.assertEqual(scale(2, 3, 4)(Point(1, 2, 3)), Point(2, 6, 12)) self.assertEqual(scale(2, 3, 4)(Vector(1, 2, 3)), Vector(2, 6, 12)) self.assertEqual(rotate(90, Vector(0, 1, 0))(Normal(1, 0, 0)), Normal(0, 0, -1))
def setUp(self): # create a transform o2w1 = translate(Vector(10, 0, 0)) * scale(1.3, 1.8, 2.0) w2o1 = o2w1.inverse() # create a sphere self.sphere1 = Sphere(o2w1, w2o1, False, 1.0, -1.0, 1.0, 360) self.primitive_sphere1 = GeometricPrimitive(self.sphere1, None, None) # create a transform o2w2 = translate(Vector(-10, 0, 0)) * scale(1.3, 1.8, 2.0) w2o2 = o2w2.inverse() # create a sphere self.sphere2 = Sphere(o2w2, w2o2, False, 1.0, -1.0, 1.0, 360) self.primitive_sphere2 = GeometricPrimitive(self.sphere2, None, None) primitives = [self.primitive_sphere1, self.primitive_sphere2] self.grid_accel = GridAccel(primitives, True)
def test_vector(self): # operator[] v = Vector(1.0, 2.0, 3.0) self.assertEqual(v[0], 1.0) self.assertEqual(v[1], 2.0) self.assertEqual(v[2], 3.0) # misc operators self.assertEqual(self.v1 * 4, 2 * self.v2) self.assertEqual(self.p2 - self.p1, self.v1) # length functions self.assertEqual(self.v1.length_squared(), 3) self.assertEqual(self.v1.length(), math.sqrt(3)) # operator[] for assignments v = Vector(1.0, 2.0, 3.0) for i in range(3): v[i] = 9.0 self.assertEqual(v[i], 9.0)
def test_inverse(self): m1 = scale(2.0, 3.0, 4.0) m2 = scale(1.0/2.0, 1.0/3.0, 1.0/4.0) self.assertEqual(m1.inverse(), m2) self.assertEqual(m1.m_inv, m2.m) self.assertEqual(m2.m_inv, m1.m) m3 = translate(Point(5, 6, 7)) * scale(2, -3 , 4) * rotate(17, Vector(-1, 4, -2)) m4 = m3.inverse() identity = Transform() self.assertTrue((m3*m4).is_identity()) self.assertTrue((m4*m3).is_identity())
def test_intersect(self): # test an intersection ray = Ray(Point(20, 10, 10), Vector(-1, -1, -1)) intersect, t_hit, ray_epsilon, dg = self.sphere.intersect(ray) self.assertTrue(intersect) intersect = self.sphere.intersect_p(ray) self.assertTrue(intersect) # test an intersection ray = Ray(Point(20, 10, 10), Vector(-1, 1, -1)) intersect, t_hit, ray_epsilon, dg = self.sphere.intersect(ray) self.assertFalse(intersect) intersect = self.sphere.intersect_p(ray) self.assertFalse(intersect) # test an intersection ray = Ray(Point(10, 0, 0), Vector(3, 1, -2)) intersect, t_hit, ray_epsilon, dg = self.sphere.intersect(ray) self.assertTrue(intersect) intersect = self.sphere.intersect_p(ray) self.assertTrue(intersect)
def decompose(matrix): """Decompose a Matrix4x4 into T, R and S components. Returns: T , Vector R , Quaternion S , Matrix4x4 """ R = Quaternion() S = Matrix4x4() # Extract translation _T_ from transformation matrix T = Vector(matrix.m[0][3], matrix.m[1][3], matrix.m[2][3]) # Compute new transformation matrix _M_ without translation M = Matrix4x4.from_matrix4x4(matrix) for i in range(3): M.m[i][3] = 0.0 M.m[3][i] = 0.0 M.m[3][3] = 1.0 # Extract rotation _R_ from transformation matrix norm = 1.0 count = 0 R = Matrix4x4.from_matrix4x4(M) while (count < 100 and norm > 0.0001): # Compute next matrix _Rnext_ in series Rnext = Matrix4x4() Rit = inverse(transpose(R)) for i in range(4): for j in range(4): Rnext.m[i][j] = 0.5 * (R.m[i][j] + Rit.m[i][j]) # Compute norm of difference between _R_ and _Rnext_ norm = 0.0 for i in range(3): n = abs(R.m[i][0] - Rnext.m[i][0]) + \ abs(R.m[i][1] - Rnext.m[i][1]) + \ abs(R.m[i][2] - Rnext.m[i][2]) norm = max(norm, n) R = Rnext count += 1 # XXX TODO FIXME deal with flip... Rquat = Quaternion.from_transform(Transform(R)) # Compute scale _S_ using rotation and original matrix S = inverse(R) * M return T, Rquat, S
def __init__(self): """Default constructor for DifferentialGeometry.""" self.p = Point() self.nn = Normal() self.u = 0.0 self.v = 0.0 self.shape = None self.dp_du = Vector() self.dp_dv = Vector() self.dn_du = Normal() self.dn_dv = Normal() self.dp_dx = Vector() self.dp_dy = Vector() self.du_dx = 0.0 self.dv_dx = 0.0 self.du_dy = 0.0 self.dv_dy = 0.0
def test_ray(self): r = Ray(Point(0, 0, 0), Vector(1, 2, 3)) # test copy constructor r2 = Ray.from_ray(r) self.assertTrue(isinstance(r2, Ray)) self.assertEqual(r2.d, r.d) # test constructor from parent ray r3 = Ray.from_ray_parent(r.o, r.d, r, r.mint) self.assertTrue(isinstance(r3, Ray)) self.assertEqual(r3.depth, r.depth + 1) # test operator() p = r(1.7) self.assertEqual(p, Point(1.7, 3.4, 5.1))
def test_normal(self): # operator[] n = Normal(1.0, 2.0, 3.0) self.assertEqual(n[0], 1.0) self.assertEqual(n[1], 2.0) self.assertEqual(n[2], 3.0) # face_forward n2 = Normal(1, 0, 0) v = Vector(-0.5, -0.1, 0.2) self.assertEqual(face_forward(n, v), -n) # operator[] for assignments n = Normal(1.0, 2.0, 3.0) for i in range(3): n[i] = 9.0 self.assertEqual(n[i], 9.0)
def test_transform_ray(self): ray_differential = RayDifferential(origin=Point(1,2,3), direction=Vector(10,20,30)) ray_differential.rx_origin = Point(4,5,6) ray_differential.ry_origin = Point(5,6,7) ray_differential.rx_direction = Vector(2,3,4) ray_differential.ry_direction = Vector(3,4,5) ray_transformed = translate(Point(10,20,30))(ray_differential) self.assertTrue(isinstance(ray_transformed, RayDifferential)) self.assertEqual(ray_transformed.o, Point(11,22,33)) self.assertEqual(ray_transformed.d, Vector(10,20,30)) self.assertEqual(ray_transformed.rx_origin, Point(14,25,36)) self.assertEqual(ray_transformed.ry_origin, Point(15,26,37)) self.assertEqual(ray_transformed.rx_direction, Vector(2,3,4)) self.assertEqual(ray_transformed.ry_direction, Vector(3,4,5))
def from_transform(cls, transform): """Constructor from a Transform.""" m = transform.m trace = m.m[0][0] + m.m[1][1] + m.m[2][2] v = Vector() w = 1.0 if trace > 0.0: # Compute w from matrix trace, then xyz # 4w^2 = m[0][0] + m[1][1] + m[2][2] + m[3][3] (but m[3][3] == 1) s = math.sqrt(trace + 1.0) w = s / 2.0 s = 0.5 / s v.x = (m.m[2][1] - m.m[1][2]) * s v.y = (m.m[0][2] - m.m[2][0]) * s v.z = (m.m[1][0] - m.m[0][1]) * s else: # Compute largest of $x$, $y$, or $z$, then remaining components q = [0.0, 0.0, 0.0] i = 0 if m.m[1][1] > m.m[0][0]: i = 1 if m.m[2][2] > m.m[i][i]: i = 2 j = (i + 1) % 3 k = (j + 1) % 3 s = math.sqrt((m.m[i][i] - (m.m[j][j] + m.m[k][k])) + 1.0) q[i] = s * 0.5 if s != 0.0: s = 0.5 / s w = (m.m[k][j] - m.m[j][k]) * s q[j] = (m.m[j][i] + m.m[i][j]) * s q[k] = (m.m[k][i] + m.m[i][k]) * s v.x = q[0] v.y = q[1] v.z = q[2] return cls(v, w)
def intersect(self, r): """Intersect the ray with the shape.""" # Transform _Ray_ to object space ray = self.world_to_object(r) # Compute quadratic sphere coefficients A = ray.d.x * ray.d.x + ray.d.y * ray.d.y + ray.d.z * ray.d.z B = 2 * (ray.d.x * ray.o.x + ray.d.y * ray.o.y + ray.d.z * ray.o.z) C = ray.o.x*ray.o.x + ray.o.y*ray.o.y + \ ray.o.z*ray.o.z - self.radius*self.radius # Solve quadratic equation for _t_ values found, t0, t1 = quadratic(A, B, C) if not found: return False, float('inf'), 0.0, None # Compute intersection distance along ray if (t0 > ray.maxt or t1 < ray.mint): return False, float('inf'), 0.0, None t_hit = t0 if (t0 < ray.mint): t_hit = t1 if (t_hit > ray.maxt): return False, float('inf'), 0.0, None # Compute sphere hit position and $\phi$ phi_t = ray(t_hit) if (phi_t.x == 0.0 and phi_t.y == 0.0): phi_t.x = 1e-5 * self.radius phi = math.atan2(phi_t.y, phi_t.x) if (phi < 0.0): phi += 2.0 * math.pi # Test sphere intersection against clipping parameters if ((self.z_min > -self.radius and phi_t.z < self.z_min) or \ (self.z_max < self.radius and phi_t.z > self.z_max) or \ phi > self.phi_max): if (t_hit == t1): return False, float('inf'), 0.0, None if (t1 > ray.maxt): return False, float('inf'), 0.0, None t_hit = t1 # Compute sphere hit position and $\phi$ phi_t = ray(t_hit) if (phi_t.x == 0.0 and phi_t.y == 0.0): phi_t.x = 1e-5 * self.radius phi = math.atan2(phi_t.y, phi_t.x) if (phi < 0.0): phi += 2.0 * math.pi if ((self.z_min > -self.radius and phi_t.z < self.z_min) or \ (self.z_max < self.radius and phi_t.z > self.z_max) or \ phi > self.phi_max): return False, float('inf'), 0.0, None # Find parametric representation of sphere hit u = phi / self.phi_max theta = math.acos(clamp(phi_t.z / self.radius, -1.0, 1.0)) v = (theta - self.theta_min) / (self.theta_max - self.theta_min) # Compute sphere $\dpdu$ and $\dpdv$ zradius = math.sqrt(phi_t.x * phi_t.x + phi_t.y * phi_t.y) inv_z_radius = 1.0 / zradius cos_phi = phi_t.x * inv_z_radius sin_phi = phi_t.y * inv_z_radius dpdu = Vector(-self.phi_max * phi_t.y, self.phi_max * phi_t.x, 0) dpdv = (self.theta_max-self.theta_min) * \ Vector(phi_t.z * cos_phi, phi_t.z * sin_phi, -self.radius * math.sin(theta)) # Compute sphere $\dndu$ and $\dndv$ d2Pduu = -self.phi_max * self.phi_max * Vector(phi_t.x, phi_t.y, 0) d2Pduv = (self.theta_max - self.theta_min) * phi_t.z * self.phi_max * \ Vector(-sin_phi, cos_phi, 0.0) d2Pdvv = -(self.theta_max - self.theta_min) * \ (self.theta_max - self.theta_min) * \ Vector(phi_t.x, phi_t.y, phi_t.z) # Compute coefficients for fundamental forms E = dot(dpdu, dpdu) F = dot(dpdu, dpdv) G = dot(dpdv, dpdv) N = normalize(cross(dpdu, dpdv)) e = dot(N, d2Pduu) f = dot(N, d2Pduv) g = dot(N, d2Pdvv) # Compute $\dndu$ and $\dndv$ from fundamental form coefficients invEGF2 = 1.0 / (E * G - F * F) dndu = Normal.from_vector((f*F - e*G) * invEGF2 * dpdu + \ (e*F - f*E) * invEGF2 * dpdv) dndv = Normal.from_vector((g*F - f*G) * invEGF2 * dpdu + \ (f*F - g*E) * invEGF2 * dpdv) # Initialize _DifferentialGeometry_ from parametric information o2w = self.object_to_world dg = DifferentialGeometry.from_intersection(o2w(phi_t), o2w(dpdu), o2w(dpdv), o2w(dndu), o2w(dndv), u, v, self) # Compute _rayEpsilon_ for quadric intersection ray_epsilon = 5e-4 * t_hit return True, t_hit, ray_epsilon, dg
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
def test_transform_handedness(self): m1 = translate(Point(-17, 2, 31)) * scale(0.5, 6 , 1.4) * rotate(35, Vector(-15, 20, 0.2)) self.assertFalse(m1.swap_handedness()) m2 = translate(Point(5, 6, 7)) * scale(2, -3 , 4) * rotate(17, Vector(-1, 4, -2)) self.assertTrue(m2.swap_handedness())
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()