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)
55 void csg_fov(float fov)
57 cam.fov = M_PI * fov / 180.0f;
61 int csg_load(const char *fname)
63 struct ts_node *root = 0, *c;
66 if(!(root = ts_load(fname))) {
67 fprintf(stderr, "failed to open %s\n", fname);
70 if(strcmp(root->name, "csgray_scene") != 0) {
71 fprintf(stderr, "invalid scene file: %s\n", fname);
77 if(strcmp(c->name, "viewer") == 0) {
78 static float def_pos[] = {0, 0, 5};
79 static float def_targ[] = {0, 0, 0};
81 float *p = ts_get_attr_vec(c, "position", def_pos);
82 float *t = ts_get_attr_vec(c, "target", def_targ);
84 csg_view(p[0], p[1], p[2], t[0], t[1], t[2]);
85 csg_fov(ts_get_attr_num(c, "fov", 50.0f));
87 } else if((o = load_object(c))) {
103 int csg_save(const char *fname)
105 return -1; /* TODO */
108 void csg_add_object(csg_object *o)
113 if(o->ob.type == OB_NULL && (o->ob.emr > 0.0f || o->ob.emg > 0.0f || o->ob.emb > 0.0f)) {
114 o->ob.plt_next = plights;
119 int csg_remove_object(csg_object *o)
121 csg_object dummy, *n;
123 dummy.ob.next = oblist;
127 if(n->ob.next == o) {
128 n->ob.next = o->ob.next;
136 void csg_free_object(csg_object *o)
144 static union csg_object *alloc_object(int type)
148 if(!(o = calloc(sizeof *o, 1))) {
152 mat4_identity(o->ob.xform);
153 mat4_identity(o->ob.inv_xform);
155 csg_emission(o, 0, 0, 0);
156 csg_color(o, 1, 1, 1);
163 csg_object *csg_null(float x, float y, float z)
167 if(!(o = alloc_object(OB_NULL))) {
171 mat4_translation(o->ob.xform, x, y, z);
175 csg_object *csg_sphere(float x, float y, float z, float r)
179 if(!(o = alloc_object(OB_SPHERE))) {
184 mat4_translation(o->ob.xform, x, y, z);
185 mat4_copy(o->ob.inv_xform, o->ob.xform);
186 mat4_inverse(o->ob.inv_xform);
190 csg_object *csg_cylinder(float x0, float y0, float z0, float x1, float y1, float z1, float r)
196 if(!(o = alloc_object(OB_CYLINDER))) {
204 if(fabs(dx) > fabs(dy) && fabs(dx) > fabs(dz)) {
206 } else if(fabs(dy) > fabs(dz)) {
213 mat4_lookat(o->ob.xform, x0, y0, z0, x1, y1, z1, 0, major == 2 ? 1 : 0, major == 2 ? 0 : 1);
214 mat4_copy(o->ob.inv_xform, o->ob.xform);
215 mat4_inverse(o->ob.inv_xform);
219 csg_object *csg_plane(float x, float y, float z, float nx, float ny, float nz)
224 if(!(o = alloc_object(OB_PLANE))) {
228 len = sqrt(nx * nx + ny * ny + nz * nz);
230 float s = 1.0f / len;
239 o->plane.d = x * nx + y * ny + z * nz;
243 csg_object *csg_box(float x, float y, float z, float xsz, float ysz, float zsz)
248 csg_object *csg_union(csg_object *a, csg_object *b)
252 if(!(o = alloc_object(OB_UNION))) {
260 csg_object *csg_intersection(csg_object *a, csg_object *b)
264 if(!(o = alloc_object(OB_INTERSECTION))) {
272 csg_object *csg_subtraction(csg_object *a, csg_object *b)
276 if(!(o = alloc_object(OB_SUBTRACTION))) {
284 void csg_ambient(float r, float g, float b)
291 void csg_emission(csg_object *o, float r, float g, float b)
298 void csg_color(csg_object *o, float r, float g, float b)
305 void csg_roughness(csg_object *o, float r)
310 void csg_opacity(csg_object *o, float p)
315 void csg_metallic(csg_object *o, int m)
320 void csg_reset_xform(csg_object *o)
322 mat4_identity(o->ob.xform);
323 mat4_identity(o->ob.inv_xform);
326 void csg_translate(csg_object *o, float x, float y, float z)
328 mat4_translate(o->ob.xform, x, y, z);
329 mat4_pre_translate(o->ob.inv_xform, -x, -y, -z);
332 void csg_rotate(csg_object *o, float angle, float x, float y, float z)
334 mat4_rotate(o->ob.xform, angle, x, y, z);
335 mat4_pre_rotate(o->ob.inv_xform, -angle, x, y, z);
338 void csg_scale(csg_object *o, float x, float y, float z)
340 mat4_scale(o->ob.xform, x, y, z);
341 mat4_pre_scale(o->ob.inv_xform, 1.0f / x, 1.0f / y, 1.0f / z);
344 void csg_lookat(csg_object *o, float x, float y, float z, float tx, float ty, float tz, float ux, float uy, float uz)
346 mat4_lookat(o->ob.xform, x, y, z, tx, ty, tz, ux, uy, uz);
347 mat4_inv_lookat(o->ob.inv_xform, x, y, z, tx, ty, tz, ux, uy, uz);
350 void csg_render_pixel(int x, int y, int width, int height, float aspect, float *color)
354 calc_primary_ray(&ray, x, y, width, height, aspect);
355 ray_trace(&ray, color);
358 void csg_render_image(float *pixels, int width, int height)
361 float aspect = (float)width / (float)height;
363 for(i=0; i<height; i++) {
364 for(j=0; j<width; j++) {
365 csg_render_pixel(j, i, width, height, aspect, pixels);
371 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect)
374 ray->dx = aspect * ((float)x / (float)w * 2.0f - 1.0f);
375 ray->dy = 1.0f - (float)y / (float)h * 2.0f;
376 ray->dz = -1.0f / tan(cam.fov * 0.5f);
383 static int ray_trace(struct ray *ray, float *col)
387 if(!find_intersection(ray, &hit)) {
388 background(col, ray);
392 shade(col, ray, &hit);
396 #define NULLXPOS(o) ((o)->ob.xform[12])
397 #define NULLYPOS(o) ((o)->ob.xform[13])
398 #define NULLZPOS(o) ((o)->ob.xform[14])
400 static void shade(float *col, struct ray *ray, struct hit *hit)
402 float ndotl, ndoth, len, falloff, spec;
403 csg_object *o, *lt = plights;
404 float dcol[3], scol[3] = {0};
405 float ldir[3], lcol[3], hdir[3];
410 dcol[0] = ambient[0];
411 dcol[1] = ambient[1];
412 dcol[2] = ambient[2];
415 ldir[0] = NULLXPOS(lt) - hit->x;
416 ldir[1] = NULLYPOS(lt) - hit->y;
417 ldir[2] = NULLZPOS(lt) - hit->z;
426 if(!find_intersection(&sray, &tmphit) || tmphit.t > 1.0f) {
427 if((len = sqrt(ldir[0] * ldir[0] + ldir[1] * ldir[1] + ldir[2] * ldir[2])) != 0.0f) {
428 float s = 1.0f / len;
433 falloff = 1.0f / (len * len);
435 lcol[0] = lt->ob.emr * falloff;
436 lcol[1] = lt->ob.emg * falloff;
437 lcol[2] = lt->ob.emb * falloff;
439 if((ndotl = hit->nx * ldir[0] + hit->ny * ldir[1] + hit->nz * ldir[2]) < 0.0f) {
443 dcol[0] += o->ob.r * lcol[0] * ndotl;
444 dcol[1] += o->ob.g * lcol[1] * ndotl;
445 dcol[2] += o->ob.b * lcol[2] * ndotl;
447 if(o->ob.roughness < 1.0f) {
448 float gloss = 1.0f - o->ob.roughness;
450 hdir[0] = ldir[0] - ray->dx;
451 hdir[1] = ldir[1] - ray->dy;
452 hdir[2] = ldir[2] - ray->dz;
453 if((len = sqrt(hdir[0] * hdir[0] + hdir[1] * hdir[1] + hdir[2] * hdir[2])) != 0.0f) {
454 float s = 1.0f / len;
460 if((ndoth = hit->nx * hdir[0] + hit->ny * hdir[1] + hit->nz * hdir[2]) < 0.0f) {
463 spec = gloss * pow(ndoth, 100.0f * gloss);
470 scol[0] += lcol[0] * spec;
471 scol[1] += lcol[1] * spec;
472 scol[2] += lcol[2] * spec;
476 lt = lt->ob.plt_next;
479 col[0] = dcol[0] + scol[0];
480 col[1] = dcol[1] + scol[1];
481 col[2] = dcol[2] + scol[2];
484 static void background(float *col, struct ray *ray)
486 col[0] = col[1] = col[2] = 0.0f;
489 static int find_intersection(struct ray *ray, struct hit *best)
493 struct hinterv *hit, *it;
500 if((hit = ray_intersect(ray, o))) {
503 if(it->end[0].t > 1e-6) {
507 if(it->end[1].t > 1e-6) {
514 if(it && it->end[idx].t < best->t) {
515 *best = it->end[idx];
525 static csg_object *load_object(struct ts_node *node)
529 csg_object *sub, *o = 0, *olist = 0, *otail = 0;
530 int num_subobj = 0, is_csgop = 0;
532 if(strcmp(node->name, "null") == 0) {
533 if(!(o = csg_null(0, 0, 0))) {
537 } else if(strcmp(node->name, "sphere") == 0) {
538 float rad = ts_get_attr_num(node, "radius", 1.0f);
539 if(!(o = csg_sphere(0, 0, 0, rad))) {
543 } else if(strcmp(node->name, "plane") == 0) {
544 static float def_norm[] = {0, 1, 0};
545 float *norm = ts_get_attr_vec(node, "normal", def_norm);
546 if(!(o = csg_plane(0, 0, 0, norm[0], norm[1], norm[2]))) {
550 } else if(strcmp(node->name, "box") == 0) {
551 static float def_sz[] = {1, 1, 1};
552 float *sz = ts_get_attr_vec(node, "size", def_sz);
553 if(!(o = csg_box(0, 0, 0, sz[0], sz[1], sz[2]))) {
557 } else if(strcmp(node->name, "union") == 0) {
558 if(!(o = csg_union(0, 0))) {
563 } else if(strcmp(node->name, "intersect") == 0) {
564 if(!(o = csg_intersection(0, 0))) {
569 } else if(strcmp(node->name, "subtract") == 0) {
570 if(!(o = csg_subtraction(0, 0))) {
580 c = node->child_list;
582 if((sub = load_object(c))) {
584 otail->ob.next = sub;
594 if(num_subobj != 2) {
598 o->un.b = olist->ob.next;
602 if((avec = ts_get_attr_vec(node, "position", 0))) {
603 csg_translate(o, avec[0], avec[1], avec[2]);
605 if((avec = ts_get_attr_vec(node, "rotaxis", 0))) {
606 csg_rotate(o, ts_get_attr_num(node, "rotangle", 0.0f), avec[0], avec[1], avec[2]);
608 if((avec = ts_get_attr_vec(node, "scaling", 0))) {
609 csg_scale(o, avec[0], avec[1], avec[2]);
611 if((avec = ts_get_attr_vec(node, "target", 0))) {
612 /* don't move this before position */
613 float def_up[] = {0, 1, 0};
614 float *up = ts_get_attr_vec(node, "up", def_up);
615 float x = o->ob.xform[12];
616 float y = o->ob.xform[13];
617 float z = o->ob.xform[14];
618 csg_lookat(o, x, y, z, avec[0], avec[1], avec[2], up[0], up[1], up[2]);
621 if((avec = ts_get_attr_vec(node, "color", 0))) {
622 csg_color(o, avec[0], avec[1], avec[2]);
624 if((avec = ts_get_attr_vec(node, "emission", 0))) {
625 csg_emission(o, avec[0], avec[1], avec[2]);
628 csg_roughness(o, ts_get_attr_num(node, "roughness", o->ob.roughness));
629 csg_opacity(o, ts_get_attr_num(node, "opacity", o->ob.opacity));
630 csg_metallic(o, ts_get_attr_int(node, "metallic", o->ob.metallic));
638 olist = olist->ob.next;