11 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect);
12 static int ray_trace(struct ray *ray, float *col);
13 static void shade(float *col, struct ray *ray, struct hit *hit);
14 static void background(float *col, struct ray *ray);
15 static int find_intersection(struct ray *ray, struct hit *best);
16 static csg_object *load_object(struct ts_node *node);
18 static float ambient[3];
19 static struct camera cam;
20 static csg_object *oblist;
21 static csg_object *plights;
29 csg_view(0, 0, 5, 0, 0, 0);
35 void csg_destroy(void)
38 csg_object *o = oblist;
39 oblist = oblist->ob.next;
45 void csg_view(float x, float y, float z, float tx, float ty, float tz)
60 len = sqrt(dir[0] * dir[0] + dir[1] * dir[1] + dir[2] * dir[2]);
62 if(1.0f - fabs(ty - y) / len < 1e-6f) {
63 cam.ux = cam.uy = 0.0f;
66 cam.ux = cam.uz = 0.0f;
70 mat4_lookat(cam.xform, x, y, z, tx, ty, tz, cam.ux, cam.uy, cam.uz);
73 void csg_fov(float fov)
75 cam.fov = M_PI * fov / 180.0f;
79 int csg_load(const char *fname)
81 struct ts_node *root = 0, *c;
84 if(!(root = ts_load(fname))) {
85 fprintf(stderr, "failed to open %s\n", fname);
88 if(strcmp(root->name, "csgray_scene") != 0) {
89 fprintf(stderr, "invalid scene file: %s\n", fname);
95 if(strcmp(c->name, "viewer") == 0) {
96 static float def_pos[] = {0, 0, 5};
97 static float def_targ[] = {0, 0, 0};
99 float *p = ts_get_attr_vec(c, "position", def_pos);
100 float *t = ts_get_attr_vec(c, "target", def_targ);
102 csg_view(p[0], p[1], p[2], t[0], t[1], t[2]);
103 csg_fov(ts_get_attr_num(c, "fov", 50.0f));
105 } else if((o = load_object(c))) {
121 int csg_save(const char *fname)
123 return -1; /* TODO */
126 void csg_add_object(csg_object *o)
131 if(o->ob.type == OB_NULL && (o->ob.emr > 0.0f || o->ob.emg > 0.0f || o->ob.emb > 0.0f)) {
132 o->ob.plt_next = plights;
137 int csg_remove_object(csg_object *o)
139 csg_object dummy, *n;
141 dummy.ob.next = oblist;
145 if(n->ob.next == o) {
146 n->ob.next = o->ob.next;
154 void csg_free_object(csg_object *o)
164 static union csg_object *alloc_object(int type)
168 if(!(o = calloc(sizeof *o, 1))) {
172 mat4_identity(o->ob.xform);
173 mat4_identity(o->ob.inv_xform);
175 csg_emission(o, 0, 0, 0);
176 csg_color(o, 1, 1, 1);
183 csg_object *csg_null(float x, float y, float z)
187 if(!(o = alloc_object(OB_NULL))) {
191 mat4_translation(o->ob.xform, x, y, z);
192 mat4_translation(o->ob.inv_xform, -x, -y, -z);
196 csg_object *csg_sphere(float x, float y, float z, float r)
200 if(!(o = alloc_object(OB_SPHERE))) {
205 mat4_translation(o->ob.xform, x, y, z);
206 mat4_translation(o->ob.inv_xform, -x, -y, -z);
210 csg_object *csg_cylinder(float x0, float y0, float z0, float x1, float y1, float z1, float r)
213 float x, y, z, dx, dy, dz;
216 if(!(o = alloc_object(OB_CYLINDER))) {
225 o->cyl.height = sqrt(dx * dx + dy * dy + dz * dz);
227 if(fabs(dx) > fabs(dy) && fabs(dx) > fabs(dz)) {
229 } else if(fabs(dy) > fabs(dz)) {
235 x = (x0 + x1) / 2.0f;
236 y = (y0 + y1) / 2.0f;
237 z = (z0 + z1) / 2.0f;
239 mat4_lookat(o->ob.xform, x, y, z, dx, dz, -dy, 0, major == 2 ? 0 : 1, major == 2 ? 1 : 0);
240 mat4_copy(o->ob.inv_xform, o->ob.xform);
241 mat4_inverse(o->ob.inv_xform);
245 csg_object *csg_plane(float x, float y, float z, float nx, float ny, float nz)
250 if(!(o = alloc_object(OB_PLANE))) {
254 len = sqrt(nx * nx + ny * ny + nz * nz);
256 float s = 1.0f / len;
267 mat4_translation(o->ob.xform, x, y, z);
268 mat4_translation(o->ob.inv_xform, -x, -y, -z);
272 csg_object *csg_box(float x, float y, float z, float xsz, float ysz, float zsz)
276 if(!(o = alloc_object(OB_BOX))) {
284 mat4_translation(o->ob.xform, x, y, z);
285 mat4_translation(o->ob.inv_xform, -x, -y, -z);
289 csg_object *csg_union(csg_object *a, csg_object *b)
293 if(!(o = alloc_object(OB_UNION))) {
301 csg_object *csg_intersection(csg_object *a, csg_object *b)
305 if(!(o = alloc_object(OB_INTERSECTION))) {
313 csg_object *csg_subtraction(csg_object *a, csg_object *b)
317 if(!(o = alloc_object(OB_SUBTRACTION))) {
325 void csg_ambient(float r, float g, float b)
332 void csg_emission(csg_object *o, float r, float g, float b)
339 void csg_color(csg_object *o, float r, float g, float b)
346 void csg_roughness(csg_object *o, float r)
351 void csg_opacity(csg_object *o, float p)
356 void csg_metallic(csg_object *o, int m)
361 void csg_reset_xform(csg_object *o)
363 mat4_identity(o->ob.xform);
364 mat4_identity(o->ob.inv_xform);
367 void csg_translate(csg_object *o, float x, float y, float z)
369 mat4_translate(o->ob.xform, x, y, z);
370 mat4_pre_translate(o->ob.inv_xform, -x, -y, -z);
373 void csg_rotate(csg_object *o, float angle, float x, float y, float z)
375 mat4_rotate(o->ob.xform, angle, x, y, z);
376 mat4_pre_rotate(o->ob.inv_xform, -angle, x, y, z);
379 void csg_scale(csg_object *o, float x, float y, float z)
381 mat4_scale(o->ob.xform, x, y, z);
382 mat4_pre_scale(o->ob.inv_xform, 1.0f / x, 1.0f / y, 1.0f / z);
385 void csg_lookat(csg_object *o, float x, float y, float z, float tx, float ty, float tz, float ux, float uy, float uz)
387 mat4_lookat(o->ob.xform, x, y, z, tx, ty, tz, ux, uy, uz);
388 mat4_inv_lookat(o->ob.inv_xform, x, y, z, tx, ty, tz, ux, uy, uz);
391 void csg_render_pixel(int x, int y, int width, int height, float aspect, float *color)
395 csg_dbg_pixel = (x == 400 && y == 186);
397 calc_primary_ray(&ray, x, y, width, height, aspect);
398 ray_trace(&ray, color);
401 void csg_render_image(float *pixels, int width, int height)
404 float aspect = (float)width / (float)height;
406 for(i=0; i<height; i++) {
407 for(j=0; j<width; j++) {
408 csg_render_pixel(j, i, width, height, aspect, pixels);
414 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect)
416 ray->dx = aspect * ((float)x / (float)w * 2.0f - 1.0f);
417 ray->dy = 1.0f - (float)y / (float)h * 2.0f;
418 ray->dz = -1.0f / tan(cam.fov * 0.5f);
424 xform_ray(ray, cam.xform);
427 static int ray_trace(struct ray *ray, float *col)
431 if(!find_intersection(ray, &hit)) {
432 background(col, ray);
436 shade(col, ray, &hit);
440 #define NULLXPOS(o) ((o)->ob.xform[12])
441 #define NULLYPOS(o) ((o)->ob.xform[13])
442 #define NULLZPOS(o) ((o)->ob.xform[14])
444 static void shade(float *col, struct ray *ray, struct hit *hit)
446 float ndotl, ndoth, len, falloff, spec;
447 csg_object *o, *lt = plights;
448 float dcol[3], scol[3] = {0};
449 float ldir[3], lcol[3], hdir[3];
454 dcol[0] = ambient[0];
455 dcol[1] = ambient[1];
456 dcol[2] = ambient[2];
459 ldir[0] = NULLXPOS(lt) - hit->x;
460 ldir[1] = NULLYPOS(lt) - hit->y;
461 ldir[2] = NULLZPOS(lt) - hit->z;
470 if(!find_intersection(&sray, &tmphit) || tmphit.t < 0.000001 || tmphit.t > 1.0f) {
471 if((len = sqrt(ldir[0] * ldir[0] + ldir[1] * ldir[1] + ldir[2] * ldir[2])) != 0.0f) {
472 float s = 1.0f / len;
477 falloff = 1.0f / (len * len);
479 lcol[0] = lt->ob.emr * falloff;
480 lcol[1] = lt->ob.emg * falloff;
481 lcol[2] = lt->ob.emb * falloff;
483 if((ndotl = hit->nx * ldir[0] + hit->ny * ldir[1] + hit->nz * ldir[2]) < 0.0f) {
487 dcol[0] += o->ob.r * lcol[0] * ndotl;
488 dcol[1] += o->ob.g * lcol[1] * ndotl;
489 dcol[2] += o->ob.b * lcol[2] * ndotl;
491 if(o->ob.roughness < 1.0f) {
492 float gloss = 1.0f - o->ob.roughness;
494 hdir[0] = ldir[0] - ray->dx;
495 hdir[1] = ldir[1] - ray->dy;
496 hdir[2] = ldir[2] - ray->dz;
497 if((len = sqrt(hdir[0] * hdir[0] + hdir[1] * hdir[1] + hdir[2] * hdir[2])) != 0.0f) {
498 float s = 1.0f / len;
504 if((ndoth = hit->nx * hdir[0] + hit->ny * hdir[1] + hit->nz * hdir[2]) < 0.0f) {
507 spec = gloss * pow(ndoth, 100.0f * gloss);
514 scol[0] += lcol[0] * spec;
515 scol[1] += lcol[1] * spec;
516 scol[2] += lcol[2] * spec;
520 lt = lt->ob.plt_next;
523 col[0] = dcol[0] + scol[0];
524 col[1] = dcol[1] + scol[1];
525 col[2] = dcol[2] + scol[2];
528 static void background(float *col, struct ray *ray)
530 col[0] = col[1] = col[2] = 0.0f;
533 static int find_intersection(struct ray *ray, struct hit *best)
537 struct hinterv *hit, *it;
544 if((hit = ray_intersect(ray, o))) {
547 if(it->end[0].t > 1e-6) {
551 if(it->end[1].t > 1e-6) {
558 if(it && it->end[idx].t < best->t) {
559 *best = it->end[idx];
569 static csg_object *load_object(struct ts_node *node)
573 csg_object *sub, *o = 0, *olist = 0, *otail = 0;
574 int num_subobj = 0, is_csgop = 0;
576 if(strcmp(node->name, "null") == 0) {
577 if(!(o = csg_null(0, 0, 0))) {
581 } else if(strcmp(node->name, "sphere") == 0) {
582 float rad = ts_get_attr_num(node, "radius", 1.0f);
583 if(!(o = csg_sphere(0, 0, 0, rad))) {
587 } else if(strcmp(node->name, "cylinder") == 0) {
588 float rad = ts_get_attr_num(node, "radius", 1.0f);
589 float height = ts_get_attr_num(node, "height", 1.0f);
590 if(!(o = csg_cylinder(0, -height/2.0f, 0, 0, height/2.0f, 0, rad))) {
594 } else if(strcmp(node->name, "plane") == 0) {
595 static float def_norm[] = {0, 1, 0};
596 float *norm = ts_get_attr_vec(node, "normal", def_norm);
597 if(!(o = csg_plane(0, 0, 0, norm[0], norm[1], norm[2]))) {
601 } else if(strcmp(node->name, "box") == 0) {
602 static float def_sz[] = {1, 1, 1};
603 float *sz = ts_get_attr_vec(node, "size", def_sz);
604 if(!(o = csg_box(0, 0, 0, sz[0], sz[1], sz[2]))) {
608 } else if(strcmp(node->name, "union") == 0) {
609 if(!(o = csg_union(0, 0))) {
614 } else if(strcmp(node->name, "intersect") == 0) {
615 if(!(o = csg_intersection(0, 0))) {
620 } else if(strcmp(node->name, "subtract") == 0) {
621 if(!(o = csg_subtraction(0, 0))) {
631 c = node->child_list;
633 if((sub = load_object(c))) {
635 otail->ob.next = sub;
645 if(num_subobj != 2) {
649 o->un.b = olist->ob.next;
653 if((avec = ts_get_attr_vec(node, "position", 0))) {
654 csg_translate(o, avec[0], avec[1], avec[2]);
656 if((avec = ts_get_attr_vec(node, "rotaxis", 0))) {
657 csg_rotate(o, ts_get_attr_num(node, "rotangle", 0.0f), avec[0], avec[1], avec[2]);
659 if((avec = ts_get_attr_vec(node, "scaling", 0))) {
660 csg_scale(o, avec[0], avec[1], avec[2]);
662 if((avec = ts_get_attr_vec(node, "target", 0))) {
663 /* don't move this before position */
664 float def_up[] = {0, 1, 0};
665 float *up = ts_get_attr_vec(node, "up", def_up);
666 float x = o->ob.xform[12];
667 float y = o->ob.xform[13];
668 float z = o->ob.xform[14];
669 csg_lookat(o, x, y, z, avec[0], avec[1], avec[2], up[0], up[1], up[2]);
672 if((avec = ts_get_attr_vec(node, "color", 0))) {
673 csg_color(o, avec[0], avec[1], avec[2]);
675 if((avec = ts_get_attr_vec(node, "emission", 0))) {
676 csg_emission(o, avec[0], avec[1], avec[2]);
679 csg_roughness(o, ts_get_attr_num(node, "roughness", o->ob.roughness));
680 csg_opacity(o, ts_get_attr_num(node, "opacity", o->ob.opacity));
681 csg_metallic(o, ts_get_attr_int(node, "metallic", o->ob.metallic));
689 olist = olist->ob.next;