added scene loading
[csgray] / src / csgray.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <float.h>
6 #include <treestore.h>
7 #include "csgimpl.h"
8 #include "matrix.h"
9 #include "geom.h"
10
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);
17
18 static float ambient[3];
19 static struct camera cam;
20 static csg_object *oblist;
21 static csg_object *plights;
22
23 int csg_init(void)
24 {
25         oblist = 0;
26         plights = 0;
27
28         csg_ambient(0, 0, 0);
29         csg_view(0, 0, 5, 0, 0, 0);
30         csg_fov(50);
31
32         return 0;
33 }
34
35 void csg_destroy(void)
36 {
37         while(oblist) {
38                 csg_object *o = oblist;
39                 oblist = oblist->ob.next;
40                 csg_free_object(o);
41         }
42         oblist = 0;
43 }
44
45 void csg_view(float x, float y, float z, float tx, float ty, float tz)
46 {
47         cam.x = x;
48         cam.y = y;
49         cam.z = z;
50         cam.tx = tx;
51         cam.ty = ty;
52         cam.tz = tz;
53 }
54
55 void csg_fov(float fov)
56 {
57         cam.fov = M_PI * fov / 180.0f;
58 }
59
60
61 int csg_load(const char *fname)
62 {
63         struct ts_node *root = 0, *c;
64         csg_object *o;
65
66         if(!(root = ts_load(fname))) {
67                 fprintf(stderr, "failed to open %s\n", fname);
68                 return -1;
69         }
70         if(strcmp(root->name, "csgray_scene") != 0) {
71                 fprintf(stderr, "invalid scene file: %s\n", fname);
72                 goto err;
73         }
74
75         c = root->child_list;
76         while(c) {
77                 if(strcmp(c->name, "viewer") == 0) {
78                         static float def_pos[] = {0, 0, 5};
79                         static float def_targ[] = {0, 0, 0};
80
81                         float *p = ts_get_attr_vec(c, "position", def_pos);
82                         float *t = ts_get_attr_vec(c, "target", def_targ);
83
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));
86
87                 } else if((o = load_object(c))) {
88                         csg_add_object(o);
89                 }
90                 c = c->next;
91         }
92
93         ts_free_tree(root);
94         return 0;
95
96 err:
97         if(root) {
98                 ts_free_tree(root);
99         }
100         return -1;
101 }
102
103 int csg_save(const char *fname)
104 {
105         return -1;      /* TODO */
106 }
107
108 void csg_add_object(csg_object *o)
109 {
110         o->ob.next = oblist;
111         oblist = o;
112
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;
115                 plights = o;
116         }
117 }
118
119 int csg_remove_object(csg_object *o)
120 {
121         csg_object dummy, *n;
122
123         dummy.ob.next = oblist;
124         n = &dummy;
125
126         while(n->ob.next) {
127                 if(n->ob.next == o) {
128                         n->ob.next = o->ob.next;
129                         return 1;
130                 }
131                 n = n->ob.next;
132         }
133         return 0;
134 }
135
136 void csg_free_object(csg_object *o)
137 {
138         if(o->ob.destroy) {
139                 o->ob.destroy(o);
140         }
141         free(o);
142 }
143
144 static union csg_object *alloc_object(int type)
145 {
146         csg_object *o;
147
148         if(!(o = calloc(sizeof *o, 1))) {
149                 return 0;
150         }
151         o->ob.type = type;
152         mat4_identity(o->ob.xform);
153         mat4_identity(o->ob.inv_xform);
154
155         csg_emission(o, 0, 0, 0);
156         csg_color(o, 1, 1, 1);
157         csg_roughness(o, 1);
158         csg_opacity(o, 1);
159
160         return o;
161 }
162
163 csg_object *csg_null(float x, float y, float z)
164 {
165         csg_object *o;
166
167         if(!(o = alloc_object(OB_NULL))) {
168                 return 0;
169         }
170
171         mat4_translation(o->ob.xform, x, y, z);
172         return o;
173 }
174
175 csg_object *csg_sphere(float x, float y, float z, float r)
176 {
177         csg_object *o;
178
179         if(!(o = alloc_object(OB_SPHERE))) {
180                 return 0;
181         }
182
183         o->sph.rad = r;
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);
187         return o;
188 }
189
190 csg_object *csg_cylinder(float x0, float y0, float z0, float x1, float y1, float z1, float r)
191 {
192         csg_object *o;
193         float dx, dy, dz;
194         int major;
195
196         if(!(o = alloc_object(OB_CYLINDER))) {
197                 return 0;
198         }
199
200         dx = x1 - x0;
201         dy = y1 - y0;
202         dz = z1 - z0;
203
204         if(fabs(dx) > fabs(dy) && fabs(dx) > fabs(dz)) {
205                 major = 0;
206         } else if(fabs(dy) > fabs(dz)) {
207                 major = 1;
208         } else {
209                 major = 2;
210         }
211
212         o->cyl.rad = r;
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);
216         return o;
217 }
218
219 csg_object *csg_plane(float x, float y, float z, float nx, float ny, float nz)
220 {
221         csg_object *o;
222         float len;
223
224         if(!(o = alloc_object(OB_PLANE))) {
225                 return 0;
226         }
227
228         len = sqrt(nx * nx + ny * ny + nz * nz);
229         if(len != 0.0f) {
230                 float s = 1.0f / len;
231                 nx *= s;
232                 ny *= s;
233                 nz *= s;
234         }
235
236         o->plane.nx = nx;
237         o->plane.ny = ny;
238         o->plane.nz = nz;
239         o->plane.d = x * nx + y * ny + z * nz;
240         return o;
241 }
242
243 csg_object *csg_box(float x, float y, float z, float xsz, float ysz, float zsz)
244 {
245         return 0;
246 }
247
248 csg_object *csg_union(csg_object *a, csg_object *b)
249 {
250         csg_object *o;
251
252         if(!(o = alloc_object(OB_UNION))) {
253                 return 0;
254         }
255         o->un.a = a;
256         o->un.b = b;
257         return o;
258 }
259
260 csg_object *csg_intersection(csg_object *a, csg_object *b)
261 {
262         csg_object *o;
263
264         if(!(o = alloc_object(OB_INTERSECTION))) {
265                 return 0;
266         }
267         o->isect.a = a;
268         o->isect.b = b;
269         return o;
270 }
271
272 csg_object *csg_subtraction(csg_object *a, csg_object *b)
273 {
274         csg_object *o;
275
276         if(!(o = alloc_object(OB_SUBTRACTION))) {
277                 return 0;
278         }
279         o->sub.a = a;
280         o->sub.b = b;
281         return o;
282 }
283
284 void csg_ambient(float r, float g, float b)
285 {
286         ambient[0] = r;
287         ambient[1] = g;
288         ambient[2] = b;
289 }
290
291 void csg_emission(csg_object *o, float r, float g, float b)
292 {
293         o->ob.emr = r;
294         o->ob.emg = g;
295         o->ob.emb = b;
296 }
297
298 void csg_color(csg_object *o, float r, float g, float b)
299 {
300         o->ob.r = r;
301         o->ob.g = g;
302         o->ob.b = b;
303 }
304
305 void csg_roughness(csg_object *o, float r)
306 {
307         o->ob.roughness = r;
308 }
309
310 void csg_opacity(csg_object *o, float p)
311 {
312         o->ob.opacity = p;
313 }
314
315 void csg_metallic(csg_object *o, int m)
316 {
317         o->ob.metallic = m;
318 }
319
320 void csg_reset_xform(csg_object *o)
321 {
322         mat4_identity(o->ob.xform);
323         mat4_identity(o->ob.inv_xform);
324 }
325
326 void csg_translate(csg_object *o, float x, float y, float z)
327 {
328         mat4_translate(o->ob.xform, x, y, z);
329         mat4_pre_translate(o->ob.inv_xform, -x, -y, -z);
330 }
331
332 void csg_rotate(csg_object *o, float angle, float x, float y, float z)
333 {
334         mat4_rotate(o->ob.xform, angle, x, y, z);
335         mat4_pre_rotate(o->ob.inv_xform, -angle, x, y, z);
336 }
337
338 void csg_scale(csg_object *o, float x, float y, float z)
339 {
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);
342 }
343
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)
345 {
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);
348 }
349
350 void csg_render_pixel(int x, int y, int width, int height, float aspect, float *color)
351 {
352         struct ray ray;
353
354         calc_primary_ray(&ray, x, y, width, height, aspect);
355         ray_trace(&ray, color);
356 }
357
358 void csg_render_image(float *pixels, int width, int height)
359 {
360         int i, j;
361         float aspect = (float)width / (float)height;
362
363         for(i=0; i<height; i++) {
364                 for(j=0; j<width; j++) {
365                         csg_render_pixel(j, i, width, height, aspect, pixels);
366                         pixels += 3;
367                 }
368         }
369 }
370
371 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect)
372 {
373         /* TODO */
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);
377
378         ray->x = cam.x;
379         ray->y = cam.y;
380         ray->z = cam.z;
381 }
382
383 static int ray_trace(struct ray *ray, float *col)
384 {
385         struct hit hit;
386
387         if(!find_intersection(ray, &hit)) {
388                 background(col, ray);
389                 return 0;
390         }
391
392         shade(col, ray, &hit);
393         return 1;
394 }
395
396 #define NULLXPOS(o)     ((o)->ob.xform[12])
397 #define NULLYPOS(o)     ((o)->ob.xform[13])
398 #define NULLZPOS(o)     ((o)->ob.xform[14])
399
400 static void shade(float *col, struct ray *ray, struct hit *hit)
401 {
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];
406         struct ray sray;
407         struct hit tmphit;
408
409         o = hit->o;
410         dcol[0] = ambient[0];
411         dcol[1] = ambient[1];
412         dcol[2] = ambient[2];
413
414         while(lt) {
415                 ldir[0] = NULLXPOS(lt) - hit->x;
416                 ldir[1] = NULLYPOS(lt) - hit->y;
417                 ldir[2] = NULLZPOS(lt) - hit->z;
418
419                 sray.x = hit->x;
420                 sray.y = hit->y;
421                 sray.z = hit->z;
422                 sray.dx = ldir[0];
423                 sray.dy = ldir[1];
424                 sray.dz = ldir[2];
425
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;
429                                 ldir[0] *= s;
430                                 ldir[1] *= s;
431                                 ldir[2] *= s;
432                         }
433                         falloff = 1.0f / (len * len);
434
435                         lcol[0] = lt->ob.emr * falloff;
436                         lcol[1] = lt->ob.emg * falloff;
437                         lcol[2] = lt->ob.emb * falloff;
438
439                         if((ndotl = hit->nx * ldir[0] + hit->ny * ldir[1] + hit->nz * ldir[2]) < 0.0f) {
440                                 ndotl = 0.0f;
441                         }
442
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;
446
447                         if(o->ob.roughness < 1.0f) {
448                                 float gloss = 1.0f - o->ob.roughness;
449
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;
455                                         hdir[0] *= s;
456                                         hdir[1] *= s;
457                                         hdir[2] *= s;
458                                 }
459
460                                 if((ndoth = hit->nx * hdir[0] + hit->ny * hdir[1] + hit->nz * hdir[2]) < 0.0f) {
461                                         ndoth = 0.0f;
462                                 }
463                                 spec = gloss * pow(ndoth, 100.0f * gloss);
464
465                                 if(o->ob.metallic) {
466                                         lcol[0] *= o->ob.r;
467                                         lcol[1] *= o->ob.g;
468                                         lcol[2] *= o->ob.b;
469                                 }
470                                 scol[0] += lcol[0] * spec;
471                                 scol[1] += lcol[1] * spec;
472                                 scol[2] += lcol[2] * spec;
473                         }
474                 }
475
476                 lt = lt->ob.plt_next;
477         }
478
479         col[0] = dcol[0] + scol[0];
480         col[1] = dcol[1] + scol[1];
481         col[2] = dcol[2] + scol[2];
482 }
483
484 static void background(float *col, struct ray *ray)
485 {
486         col[0] = col[1] = col[2] = 0.0f;
487 }
488
489 static int find_intersection(struct ray *ray, struct hit *best)
490 {
491         int idx = 0;
492         csg_object *o;
493         struct hinterv *hit, *it;
494
495         best->t = FLT_MAX;
496         best->o = 0;
497
498         o = oblist;
499         while(o) {
500                 if((hit = ray_intersect(ray, o))) {
501                         it = hit;
502                         while(it) {
503                                 if(it->end[0].t > 1e-6) {
504                                         idx = 0;
505                                         break;
506                                 }
507                                 if(it->end[1].t > 1e-6) {
508                                         idx = 1;
509                                         break;
510                                 }
511                                 it = it->next;
512                         }
513
514                         if(it && it->end[idx].t < best->t) {
515                                 *best = it->end[idx];
516                         }
517                 }
518                 free_hit_list(hit);
519                 o = o->ob.next;
520         }
521
522         return best->o != 0;
523 }
524
525 static csg_object *load_object(struct ts_node *node)
526 {
527         float *avec;
528         struct ts_node *c;
529         csg_object *sub, *o = 0, *olist = 0, *otail = 0;
530         int num_subobj = 0, is_csgop = 0;
531
532         if(strcmp(node->name, "null") == 0) {
533                 if(!(o = csg_null(0, 0, 0))) {
534                         goto err;
535                 }
536
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))) {
540                         goto err;
541                 }
542
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]))) {
547                         goto err;
548                 }
549
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]))) {
554                         goto err;
555                 }
556
557         } else if(strcmp(node->name, "union") == 0) {
558                 if(!(o = csg_union(0, 0))) {
559                         goto err;
560                 }
561                 is_csgop = 1;
562
563         } else if(strcmp(node->name, "intersect") == 0) {
564                 if(!(o = csg_intersection(0, 0))) {
565                         goto err;
566                 }
567                 is_csgop = 1;
568
569         } else if(strcmp(node->name, "subtract") == 0) {
570                 if(!(o = csg_subtraction(0, 0))) {
571                         goto err;
572                 }
573                 is_csgop = 1;
574
575         } else {
576                 goto err;
577         }
578
579         if(is_csgop) {
580                 c = node->child_list;
581                 while(c) {
582                         if((sub = load_object(c))) {
583                                 if(olist) {
584                                         otail->ob.next = sub;
585                                         otail = sub;
586                                 } else {
587                                         olist = otail = sub;
588                                 }
589                                 ++num_subobj;
590                         }
591                         c = c->next;
592                 }
593
594                 if(num_subobj != 2) {
595                         goto err;
596                 }
597                 o->un.a = olist;
598                 o->un.b = olist->ob.next;
599                 olist->ob.next = 0;
600         }
601
602         if((avec = ts_get_attr_vec(node, "position", 0))) {
603                 csg_translate(o, avec[0], avec[1], avec[2]);
604         }
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]);
607         }
608         if((avec = ts_get_attr_vec(node, "scaling", 0))) {
609                 csg_scale(o, avec[0], avec[1], avec[2]);
610         }
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]);
619         }
620
621         if((avec = ts_get_attr_vec(node, "color", 0))) {
622                 csg_color(o, avec[0], avec[1], avec[2]);
623         }
624         if((avec = ts_get_attr_vec(node, "emission", 0))) {
625                 csg_emission(o, avec[0], avec[1], avec[2]);
626         }
627
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));
631
632         return o;
633
634 err:
635         csg_free_object(o);
636         while(olist) {
637                 o = olist;
638                 olist = olist->ob.next;
639                 csg_free_object(o);
640         }
641         return 0;
642 }