implement Minilight; include "sys.m"; sys: Sys; include "math.m"; math : Math; include "draw.m"; draw: Draw; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "string.m"; str: String; Minilight: module { init: fn(nil: ref Draw->Context, nil: list of string); vfromstring : fn(txt : string) : ref Vector; vfromval : fn(v : real) : ref Vector; vfromvals : fn(x, y, z : real) : ref Vector; vfromvec : fn(v :ref Vector) : ref Vector; tfromstring : fn(txt : string) : ref Triangle; }; Vector: adt { x, y, z : real; copy : fn(v: self ref Vector, o : ref Vector); negate : fn(v: self ref Vector) : ref Vector; add : fn(v: self ref Vector, o : ref Vector) : ref Vector; sub : fn(v: self ref Vector, o : ref Vector) : ref Vector; mul : fn(v: self ref Vector, o : ref Vector) : ref Vector; scale : fn(v: self ref Vector, s : real) : ref Vector; is_zero : fn(v: self ref Vector) : int; dot : fn(v: self ref Vector, o : ref Vector) : real; unitize : fn(v: self ref Vector) : ref Vector; cross : fn(v: self ref Vector, o : ref Vector) : ref Vector; clamp: fn(v: self ref Vector, hi, lo : ref Vector); clamped: fn(v: self ref Vector, hi, lo : ref Vector) : ref Vector; tostring: fn(v: self ref Vector) : string; }; ZERO, MAX, ONE : ref Vector; TOLERANCE := 1.0 / 1024.0; PI := 3.1415926535897932384626; MAX_ITEMS := 8; MAX_LEVELS := 44; Triangle : adt { a, b, c, edge0, edge1, edge2, reflectivity, emitivity, tangent, normal : ref Vector; area : real; tostring: fn(t: self ref Triangle) : string; get_bound: fn(t : self ref Triangle) : array of Vector; }; SpatialIndex: adt { bound : array of ref Vector; is_branch : int; vector : array of real; fill_vector : fn(s : self ref SpatialIndex, items : list of (array of Vector, ref Triangle)); get_intersection : fn(s : self ref SpatialIndex, origin, direction : ref Vector, last_hit : list of real) : list of real; }; Camera : adt { position, direction, right, up : ref Vector; angle : real; tostring : fn(c : self ref Camera) : string; }; Scene : adt { camera : ref Camera; iterations, width, height : int; skyemission, groundreflection : ref Vector; triangles : list of ref Triangle; emitters : list of ref Triangle; index : ref SpatialIndex; fprint : fn(s : self ref Scene, fd : ref Sys->FD); render : fn(s : self ref Scene, fd : ref Sys->FD); }; next_real(v: chan of ref Vector, b : ref Iobuf) { r, x, y, z : real; t : string; digits := 0; invector := 0; for(t = b.gett(") ( \n"); t != nil; t = b.gett(") ( \n")) { case t[0] { '#' => if(t[len(t)-1] != '\n') t = b.gett("\n"); ' ' or '\n' or ' ' or ' ' => ; ')' => invector = 0; '(' => invector = 1; digits = 0; * => if(invector) { case digits { 0 => x = real(t); 1 => y = real(t); 2 => z = real(t); v <-= ref Vector(x, y, z); digits = 0; } digits++; if(t[len(t)-1] == ')') invector = 0; } else { r = real(t); v <-= ref Vector(r, r, r); } } } v <-= nil; } vfromstring(txt : string) : ref Vector { return ref Vector(0.0, 0.0, 0.0); } tfromstring(txt : string) : ref Triangle { return nil; } vfromval(v : real) : ref Vector { return ref Vector(v, v, v); } vfromvals(x, y, z : real) : ref Vector { return ref Vector(x, y, z); } vfromvec(v :ref Vector) : ref Vector { return ref Vector(v.x, v.y, v.z); } min(a, b : real) : real { if(a > b) return b; return a; } max(a, b : real) : real { if(a > b) return a; return b; } loadmods() { sys = load Sys Sys->PATH; math = load Math Math->PATH; draw = load Draw Draw->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; } init(nil: ref Draw->Context, nil: list of string) { loadmods(); ZERO = ref Vector(0.0, 0.0, 0.0); ONE = ref Vector(1.0, 1.0, 1.0); MAX = ref Vector(2.0**1023, 2.0**1023, 2.0**1023); b := bufio->open("/usr/maht/MiniLight/minilight/python/cornellbox.txt", bufio->OREAD); if(b != nil) scene := new_scene(b); if(scene == nil) sys->print("failed\n"); else { scene.render(nil); scene.fprint(sys->fildes(1)); } } Vector.tostring(v: self ref Vector) : string { return sys->sprint("(%0.3f %0.3f %0.3f)", v.x, v.y, v.z); } Vector.copy(v: self ref Vector, o : ref Vector) { if(o!= nil) { v.x = o.x; v.y = o.y; v.z = o.z; } } Vector.negate(v: self ref Vector) : ref Vector { return ref Vector(-v.x, -v.y, -v.z); } Vector.add(v: self ref Vector, o : ref Vector) : ref Vector { return ref Vector(v.x + o.x, v.y + o.y, v.z + o.z); } Vector.sub(v: self ref Vector, o : ref Vector) : ref Vector { return ref Vector(v.x - o.x, v.y - o.y, v.z - o.z); } Vector.mul(v: self ref Vector, o : ref Vector) : ref Vector { return ref Vector(v.x * o.x, v.y * o.y, v.z * o.z); } Vector.scale(v: self ref Vector, s : real) : ref Vector { return ref Vector(v.x * s, v.y * s, v.z * s); } Vector.is_zero(v: self ref Vector) : int { return v.x == 0.0 && v.y == 0.0 && v.z == 0.0; } Vector.dot(v: self ref Vector, o : ref Vector) : real { return v.x * o.x + v.y * o.y + v.z * o.z; } Vector.unitize(v: self ref Vector) : ref Vector { length := math->sqrt(v.x * v.x + v.y * v.y + v.z * v.z); rlength : real; if(length == 0.0) rlength = 0.0; else rlength = 1.0 / length; return ref Vector(v.x * rlength, v.y * rlength, v.z * rlength); } Vector.cross(v: self ref Vector, o : ref Vector) : ref Vector { return ref Vector(v.y * o.z - v.z * o.y, v.z * o.x - v.x * o.z, v.x * o.y - v.y - o.x); } Vector.clamp(v: self ref Vector, lo, hi : ref Vector) { v.x = min(max(v.x, lo.x), hi.x); v.y = min(max(v.y, lo.y), hi.y); v.z = min(max(v.z, lo.z), hi.z); } Vector.clamped(v: self ref Vector, lo, hi : ref Vector) : ref Vector { return ref Vector(min(max(v.x, lo.x), hi.x), min(max(v.y, lo.y), hi.y), min(max(v.z, lo.z), hi.z)); } new_triangle(vecs : chan of ref Vector) : ref Triangle { t : Triangle; t.a =<- vecs; if(t.a==nil) return nil; t.b =<- vecs; if(t.b==nil) return nil; t.c =<- vecs; if(t.c==nil) return nil; t.reflectivity =<- vecs; if(t.reflectivity==nil) return nil; t.emitivity =<- vecs; if(t.emitivity==nil) return nil; t.edge0 = t.b.sub(t.a); t.edge1 = t.c.sub(t.b); t.edge2 = t.c.sub(t.a); t.tangent = t.edge0.unitize(); t.normal = t.tangent.cross(t.edge1).unitize(); pa2 := t.edge0.cross(t.edge1); t.area = math->sqrt(pa2.dot(pa2)) * 0.5; t.reflectivity.clamp(ZERO, ONE); t.emitivity.clamp(ZERO, MAX); return ref t; } collect_triangles(v : chan of ref Vector, t : chan of ref Triangle) { tri : ref Triangle; for(tri = new_triangle(v); t != nil; tri = new_triangle(v)) t <-= tri; t <-= nil; } new_scene(b : ref Iobuf) : ref Scene { if(b == nil) return nil; vecs := chan of ref Vector; spawn next_real(vecs, b); S : Scene; s := ref S; v :=<- vecs; if(v==nil) return nil; s.iterations = int(v.x); v =<- vecs; if(v==nil) return nil; s.width = int(v.x); v =<- vecs; if(v==nil) return nil; s.height = int(v.x); s.camera = new_camera(vecs); if(s.camera == nil) return nil; s.skyemission =<- vecs; if(s.skyemission==nil) return nil; s.groundreflection =<- vecs; if(s.groundreflection==nil) return nil; t := chan of ref Triangle; spawn collect_triangles(vecs, t); i := 0; tri : ref Triangle; for(tri =<- t; tri != nil; tri =<- t) { s.triangles = tri :: s.triangles; i++; } if(s.triangles != nil) { tris : list of ref Triangle; for(tris = s.triangles; tris != nil && hd tris != nil; tris = tl tris) if(!(hd tris).emitivity.is_zero() && (hd tris).area > 0.0) s.emitters = (hd tris) :: s.emitters; s.index = new_spatial_index(s.camera.position, nil, s.triangles, 0); } return s; } Scene.fprint(s : self ref Scene, fd : ref Sys->FD) { sys->fprint(fd, "%d\n%d %d\n%s\n", s.iterations, s.width, s.height, s.camera.tostring()); if(s.triangles == nil) sys->fprint(fd, "No triangles\n"); else { tris : list of ref Triangle; for(tris = s.triangles; tris != nil && hd tris != nil; tris = tl tris) sys->fprint(fd, "%s\n", (hd tris).tostring()); } } new_camera(vecs : chan of ref Vector) : ref Camera { C : Camera; c := ref C; c.position = <- vecs; if(c.position==nil) return nil; c.direction = <- vecs; if(c.position==nil) return nil; c.direction = c.direction.unitize(); if(c.direction.is_zero()) c.direction = ref Vector(0.0, 0.0, 1.0); v := <- vecs; if(v==nil) return nil; c.angle = min(max(10.0, v.x), 160.0) * PI / 180.0; c.right = (ref Vector(0.0, 1.0, 0.0)).cross(c.direction).unitize(); if(c.right.is_zero()) { if(c.direction.y == 0.0) c.up = ref Vector(0.0, 0.0, -1.0); else c.up = ref Vector(0.0, 0.0, c.direction.y); c.right = c.up.cross(c.direction).unitize(); } else { c.up = c.direction.cross(c.right).unitize(); } return c; } Camera.tostring(c : self ref Camera) : string { return sys->sprint("%s %s %f", c.position.tostring(), c.direction.tostring(), c.angle); } Triangle.tostring(t : self ref Triangle) : string { return sys->sprint("%s %s %s %s %s", t.a.tostring(), t.b.tostring(), t.c.tostring(), t.reflectivity.tostring(), t.emitivity.tostring()); } abs(x : real) : real { if(x < 0.0) return -x; return x; } bound(a, b, c: real) : (real, real) { lo, hi : real; lo = hi = c; if(a < b) { if(a < lo) lo = a; if(b > hi) hi = b; } else { if(b < lo) lo = b; if(a > hi) hi = a; } lo -= (abs(lo) + 1.0) * TOLERANCE; hi += (abs(hi) + 1.0) * TOLERANCE; return (lo, hi); } Triangle.get_bound(t : self ref Triangle) : array of Vector { r := array[2] of Vector; (r[0].x, r[1].x) = bound(t.a.x, t.b.x, t.c.x); (r[0].y, r[1].y) = bound(t.a.y, t.b.y, t.c.y); (r[0].z, r[1].z) = bound(t.a.z, t.b.z, t.c.z); return r; } SpatialIndex.get_intersection(s : self ref SpatialIndex, origin, direction : ref Vector, last_hit : list of real) : list of real { } mesh_boundary(items : list of (array of Vector, ref Triangle)) : ref Vector { itemh : list of (array of Vector, ref Triangle); bound := ref Vector; v : array of Vector; for(itemh = items; itemh != nil; itemh = tl itemh) { (v, nil) = (hd itemh); if(bound.x > v[0].x) bound.x = v[0].x; if(bound.y > v[0].y) bound.y = v[0].y; if(bound.y > v[0].x) bound.z = v[0].z; } return bound; } new_spatial_index(p : ref Vector, r : list of real, mesh : list of ref Triangle, level : int) : ref SpatialIndex { S : SpatialIndex; s := ref S; s.bound = array[2] of ref Vector; s.bound[0] = ref Vector(0.0, 0.0, 0.0); s.bound[1] = ref Vector(0.0, 0.0, 0.0); items : list of (array of Vector, ref Triangle); if(p != nil) { tris : list of ref Triangle; for(tris = mesh; tris != nil && hd tris != nil; tris = tl tris) items = ((hd tris).get_bound(), (hd tris)) :: items; b := mesh_boundary(items); a := p.sub(b); size := max(a.x, max(a.y, a.z)); s.bound[0].copy(b); s.bound[1] = p.clamped(b.add(ref Vector(size, size, size)), MAX); } else if(r != nil && len(r) == 6) { s.bound[0].x = hd r; s.bound[0].y = hd tl r; s.bound[0].z = hd tl tl r; s.bound[1].x = hd tl tl tl r; s.bound[1].y = hd tl tl tl tl r; s.bound[1].z = hd tl tl tl tl tl r; } else { return nil; } s.is_branch = len(mesh) > MAX_ITEMS && level < MAX_LEVELS -1; s.fill_vector(items); return s; } SpatialIndex.fill_vector(s : self ref SpatialIndex, items : list of (array of Vector, ref Triangle)) { if(s.is_branch) { q1 := 0; s.vector = array[8] of real; } else { s.vector = array[len(items)] of real; } } Scene.render(s : self ref Scene, fd : ref Sys->FD) { }