open Point open Vector type surface_result = { color: Point.t; kd: float; ks: float; phong: float } type surface_function = SurfFun of (int -> float -> float -> surface_result) | SurfConst of surface_result | SurfArray of surface_result array type base_object_kind = Cone | Cube | Cylinder | Plane | Sphere type base_object = { kind: base_object_kind; surf: surface_function; world2obj: Matrix.t; obj2world: Matrix.t; max_scale_applied : float; } type obj_desc = Base of base_object | Union of t * t | Intersection of t * t | Difference of t * t | Null (* Empty intersection *) and t = { desc : obj_desc; center : Point.t; radius : float; } let print_base_object b = begin match b.kind with Cone -> print_string "cone" | Cube -> print_string "cube" | Cylinder -> print_string "cylinder" | Plane -> print_string "plane" | Sphere -> print_string "sphere" end let not_computed = { x=0.; y = 0.; z = 0. } let obj_template = { desc = Null; (* this object is also used for Null object *) center = not_computed; radius = 0.0 } let cone f = let id, invid = Matrix.fudge_id () in { obj_template with desc = Base { kind = Cone; surf = f; world2obj = id; obj2world = invid; max_scale_applied = 1.; } } let cube f = let id, invid = Matrix.fudge_id () in { obj_template with desc = Base { kind = Cube; surf = f; world2obj = id; obj2world = invid; max_scale_applied = 1.; } } let cylinder f = let id, invid = Matrix.fudge_id () in { obj_template with desc = Base { kind = Cylinder; surf = f; world2obj = id; obj2world = invid; max_scale_applied = 1.; } } let plane f = (* let id, invid = Matrix.fudge_id () in do not fudge planes *) let id = Matrix.id and invid = Matrix.id in { obj_template with desc = Base { kind = Plane; surf = f; world2obj = id; obj2world = invid; max_scale_applied = 1.; } } let sphere f = let id, invid = Matrix.fudge_id () in { obj_template with desc = Base { kind = Sphere; surf = f; world2obj = id; obj2world = invid; max_scale_applied = 1.; } } let rec transform obj t tinv scale = match obj.desc with Base b -> { obj_template with desc = Base {b with world2obj = Matrix.compose b.world2obj tinv; obj2world = Matrix.compose t b.obj2world; max_scale_applied = b.max_scale_applied *. scale } } | Union(o1, o2) -> { obj_template with desc = Union(transform o1 t tinv scale, transform o2 t tinv scale) } | Intersection(o1, o2) -> { obj_template with desc = Intersection(transform o1 t tinv scale, transform o2 t tinv scale) } | Difference(o1, o2) -> { obj_template with desc = Difference(transform o1 t tinv scale, transform o2 t tinv scale); } | Null -> obj let rotatex obj angle = transform obj (Matrix.rotatex angle) (Matrix.rotatex (-. angle)) 1. let rotatey obj angle = transform obj (Matrix.rotatey angle) (Matrix.rotatey (-. angle)) 1. let rotatez obj angle = transform obj (Matrix.rotatez angle) (Matrix.rotatez (-. angle)) 1. let scale obj sx sy sz = transform obj (Matrix.scale sx sy sz) (Matrix.scale (1.0 /. sx) (1.0 /. sy) (1.0 /. sz)) (max (max sx sy) sz) let uscale obj s = let sinv = 1.0 /. s in transform obj (Matrix.scale s s s) (Matrix.scale sinv sinv sinv) s let translate obj tx ty tz = transform obj (Matrix.translate tx ty tz) (Matrix.translate (-. tx) (-. ty) (-. tz)) 1. let difference obj1 obj2 = { obj_template with desc = Difference(obj1, obj2)} let intersect obj1 obj2 = { obj_template with desc = Intersection(obj1, obj2)} let union obj1 obj2 = { obj_template with desc = Union(obj1, obj2)} (* Return a normal vector for the given object at the given point. Point and vector in object coordinates. *) let normal_vector_object obj p face = match obj.kind with Cone -> if face = 0 then {dx = p.x; dy = -. p.y; dz = p.z} else {dx = 0.0; dy = 1.0; dz = 0.0} | Cube -> begin match face with 0 -> {dx = 0.0; dy = 0.0; dz = -1.0} | 1 -> {dx = 0.0; dy = 0.0; dz = 1.0} | 2 -> {dx = -1.0; dy = 0.0; dz = 0.0} | 3 -> {dx = 1.0; dy = 0.0; dz = 0.0} | 4 -> {dx = 0.0; dy = 1.0; dz = 0.0} | 5 -> {dx = 0.0; dy = -1.0; dz = 0.0} | _ -> assert false end | Cylinder -> begin match face with 0 -> {dx = p.x; dy = 0.0; dz = p.z} | 1 -> {dx = 0.0; dy = 1.0; dz = 0.0} | 2 -> {dx = 0.0; dy = -1.0; dz = 0.0} | _ -> assert false end | Plane -> {dx = 0.0; dy = 1.0; dz = 0.0} | Sphere -> {dx = p.x; dy = p.y; dz = p.z} (* Given a vector, return two non-colinear vectors in the plane orthogonal to this vector. (I.e. given a normal vector, return two tangent vectors.) The product of the two tangent vectors must point in the same direction as the normal vector. *) let tangent_vectors n = if n.dy > 0.0 then ({dx = n.dy; dy = -. n.dx; dz = 0.0}, {dx = 0.0; dy = n.dz; dz = -. n.dy}) (* y 0 xy x -x ^ z = yy = y y 0 -y yz z *) else if n.dy = 0.0 then ({dx = n.dz; dy = 0.0; dz = -. n.dx}, {dx = n.dz; dy = 1.0; dz = -. n.dx}) (* z z x x 0 ^ 1 = 0 = 1 y -x -x z z *) else ({dx = n.dy; dy = -. n.dx; dz = 0.0}, {dx = 0.0; dy = -. n.dz; dz = n.dy}) (* y 0 -xy x -x ^ -z = -yy = (-y) y 0 y -zy z *) (* Normal vectors are not preserved by transformations (think non-uniform scaling), but tangent vectors are. Hence we find two tangent vectors, transform them, and take their product. *) let normal_vector obj p face = let pobj = Matrix.apply_to_point obj.world2obj p in let nobj = normal_vector_object obj pobj face in let (tang_obj1, tang_obj2) = tangent_vectors nobj in let tang_world1 = Matrix.apply_to_vect obj.obj2world tang_obj1 and tang_world2 = Matrix.apply_to_vect obj.obj2world tang_obj2 in Vector.normalize (Vector.product tang_world1 tang_world2)