35bb5a84bd6bf8ac640349458108ab5c6412ebc0
[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 "csgimpl.h"
7 #include "matrix.h"
8 #include "geom.h"
9
10 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect);
11 static int ray_trace(struct ray *ray, float *col);
12 static void shade(float *col, struct ray *ray, struct hit *hit);
13 static void background(float *col, struct ray *ray);
14 static int find_intersection(struct ray *ray, struct hit *best);
15
16 static float ambient[3];
17 static struct camera cam;
18 static csg_object *oblist;
19 static csg_object *plights;
20
21 int csg_init(void)
22 {
23         oblist = 0;
24         plights = 0;
25
26         csg_ambient(0, 0, 0);
27         csg_view(0, 0, 5, 0, 0, 0);
28         csg_fov(50);
29
30         return 0;
31 }
32
33 void csg_destroy(void)
34 {
35         while(oblist) {
36                 csg_object *o = oblist;
37                 oblist = oblist->ob.next;
38                 csg_free_object(o);
39         }
40         oblist = 0;
41 }
42
43 void csg_view(float x, float y, float z, float tx, float ty, float tz)
44 {
45         cam.x = x;
46         cam.y = y;
47         cam.z = z;
48         cam.tx = tx;
49         cam.ty = ty;
50         cam.tz = tz;
51 }
52
53 void csg_fov(float fov)
54 {
55         cam.fov = M_PI * fov / 180.0f;
56 }
57
58
59 int csg_load(const char *fname)
60 {
61         return 0;       /* TODO */
62 }
63
64 int csg_save(const char *fname)
65 {
66         return 0;       /* TODO */
67 }
68
69 void csg_add_object(csg_object *o)
70 {
71         o->ob.next = oblist;
72         oblist = o;
73
74         if(o->ob.type == OB_NULL && (o->ob.emr > 0.0f || o->ob.emg > 0.0f || o->ob.emb > 0.0f)) {
75                 o->ob.plt_next = plights;
76                 plights = o;
77         }
78 }
79
80 int csg_remove_object(csg_object *o)
81 {
82         csg_object dummy, *n;
83
84         dummy.ob.next = oblist;
85         n = &dummy;
86
87         while(n->ob.next) {
88                 if(n->ob.next == o) {
89                         n->ob.next = o->ob.next;
90                         return 1;
91                 }
92                 n = n->ob.next;
93         }
94         return 0;
95 }
96
97 void csg_free_object(csg_object *o)
98 {
99         if(o->ob.destroy) {
100                 o->ob.destroy(o);
101         }
102         free(o);
103 }
104
105 static union csg_object *alloc_object(int type)
106 {
107         csg_object *o;
108
109         if(!(o = calloc(sizeof *o, 1))) {
110                 return 0;
111         }
112         o->ob.type = type;
113         mat4_identity(o->ob.xform);
114         mat4_identity(o->ob.inv_xform);
115
116         csg_emission(o, 0, 0, 0);
117         csg_color(o, 1, 1, 1);
118         csg_roughness(o, 1);
119         csg_opacity(o, 1);
120
121         return o;
122 }
123
124 csg_object *csg_null(float x, float y, float z)
125 {
126         csg_object *o;
127
128         if(!(o = alloc_object(OB_NULL))) {
129                 return 0;
130         }
131
132         mat4_translation(o->ob.xform, x, y, z);
133         return o;
134 }
135
136 csg_object *csg_sphere(float x, float y, float z, float r)
137 {
138         csg_object *o;
139
140         if(!(o = alloc_object(OB_SPHERE))) {
141                 return 0;
142         }
143
144         o->sph.rad = r;
145         mat4_translation(o->ob.xform, x, y, z);
146         mat4_copy(o->ob.inv_xform, o->ob.xform);
147         mat4_inverse(o->ob.inv_xform);
148         return o;
149 }
150
151 csg_object *csg_cylinder(float x0, float y0, float z0, float x1, float y1, float z1, float r)
152 {
153         csg_object *o;
154         float dx, dy, dz;
155         int major;
156
157         if(!(o = alloc_object(OB_CYLINDER))) {
158                 return 0;
159         }
160
161         dx = x1 - x0;
162         dy = y1 - y0;
163         dz = z1 - z0;
164
165         if(fabs(dx) > fabs(dy) && fabs(dx) > fabs(dz)) {
166                 major = 0;
167         } else if(fabs(dy) > fabs(dz)) {
168                 major = 1;
169         } else {
170                 major = 2;
171         }
172
173         o->cyl.rad = r;
174         mat4_lookat(o->ob.xform, x0, y0, z0, x1, y1, z1, 0, major == 2 ? 1 : 0, major == 2 ? 0 : 1);
175         mat4_copy(o->ob.inv_xform, o->ob.xform);
176         mat4_inverse(o->ob.inv_xform);
177         return o;
178 }
179
180 csg_object *csg_plane(float x, float y, float z, float nx, float ny, float nz)
181 {
182         csg_object *o;
183         float len;
184
185         if(!(o = alloc_object(OB_PLANE))) {
186                 return 0;
187         }
188
189         len = sqrt(nx * nx + ny * ny + nz * nz);
190         if(len != 0.0f) {
191                 float s = 1.0f / len;
192                 nx *= s;
193                 ny *= s;
194                 nz *= s;
195         }
196
197         o->plane.nx = nx;
198         o->plane.ny = ny;
199         o->plane.nz = nz;
200         o->plane.d = x * nx + y * ny + z * nz;
201         return o;
202 }
203
204 csg_object *csg_box(float x, float y, float z, float xsz, float ysz, float zsz)
205 {
206         return 0;
207 }
208
209 csg_object *csg_union(csg_object *a, csg_object *b)
210 {
211         csg_object *o;
212
213         if(!(o = alloc_object(OB_UNION))) {
214                 return 0;
215         }
216         o->un.a = a;
217         o->un.b = b;
218         return o;
219 }
220
221 csg_object *csg_intersection(csg_object *a, csg_object *b)
222 {
223         csg_object *o;
224
225         if(!(o = alloc_object(OB_INTERSECTION))) {
226                 return 0;
227         }
228         o->isect.a = a;
229         o->isect.b = b;
230         return o;
231 }
232
233 csg_object *csg_subtraction(csg_object *a, csg_object *b)
234 {
235         csg_object *o;
236
237         if(!(o = alloc_object(OB_SUBTRACTION))) {
238                 return 0;
239         }
240         o->sub.a = a;
241         o->sub.b = b;
242         return o;
243 }
244
245 void csg_ambient(float r, float g, float b)
246 {
247         ambient[0] = r;
248         ambient[1] = g;
249         ambient[2] = b;
250 }
251
252 void csg_emission(csg_object *o, float r, float g, float b)
253 {
254         o->ob.emr = r;
255         o->ob.emg = g;
256         o->ob.emb = b;
257 }
258
259 void csg_color(csg_object *o, float r, float g, float b)
260 {
261         o->ob.r = r;
262         o->ob.g = g;
263         o->ob.b = b;
264 }
265
266 void csg_roughness(csg_object *o, float r)
267 {
268         o->ob.roughness = r;
269 }
270
271 void csg_opacity(csg_object *o, float p)
272 {
273         o->ob.opacity = p;
274 }
275
276 void csg_metallic(csg_object *o, int m)
277 {
278         o->ob.metallic = m;
279 }
280
281
282 void csg_render_pixel(int x, int y, int width, int height, float aspect, float *color)
283 {
284         struct ray ray;
285
286         calc_primary_ray(&ray, x, y, width, height, aspect);
287         ray_trace(&ray, color);
288 }
289
290 void csg_render_image(float *pixels, int width, int height)
291 {
292         int i, j;
293         float aspect = (float)width / (float)height;
294
295         for(i=0; i<height; i++) {
296                 for(j=0; j<width; j++) {
297                         csg_render_pixel(j, i, width, height, aspect, pixels);
298                         pixels += 3;
299                 }
300         }
301 }
302
303 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect)
304 {
305         /* TODO */
306         ray->dx = aspect * ((float)x / (float)w * 2.0f - 1.0f);
307         ray->dy = 1.0f - (float)y / (float)h * 2.0f;
308         ray->dz = -1.0f / tan(cam.fov * 0.5f);
309
310         ray->x = cam.x;
311         ray->y = cam.y;
312         ray->z = cam.z;
313 }
314
315 static int ray_trace(struct ray *ray, float *col)
316 {
317         struct hit hit;
318
319         if(!find_intersection(ray, &hit)) {
320                 background(col, ray);
321                 return 0;
322         }
323
324         shade(col, ray, &hit);
325         return 1;
326 }
327
328 #define NULLXPOS(o)     ((o)->ob.xform[12])
329 #define NULLYPOS(o)     ((o)->ob.xform[13])
330 #define NULLZPOS(o)     ((o)->ob.xform[14])
331
332 static void shade(float *col, struct ray *ray, struct hit *hit)
333 {
334         float ndotl, ndoth, len, falloff, spec;
335         csg_object *o, *lt = plights;
336         float dcol[3], scol[3] = {0};
337         float ldir[3], lcol[3], hdir[3];
338         struct ray sray;
339         struct hit tmphit;
340
341         o = hit->o;
342         dcol[0] = ambient[0];
343         dcol[1] = ambient[1];
344         dcol[2] = ambient[2];
345
346         while(lt) {
347                 ldir[0] = NULLXPOS(lt) - hit->x;
348                 ldir[1] = NULLYPOS(lt) - hit->y;
349                 ldir[2] = NULLZPOS(lt) - hit->z;
350
351                 sray.x = hit->x;
352                 sray.y = hit->y;
353                 sray.z = hit->z;
354                 sray.dx = ldir[0];
355                 sray.dy = ldir[1];
356                 sray.dz = ldir[2];
357
358                 if(!find_intersection(&sray, &tmphit) || tmphit.t > 1.0f) {
359                         if((len = sqrt(ldir[0] * ldir[0] + ldir[1] * ldir[1] + ldir[2] * ldir[2])) != 0.0f) {
360                                 float s = 1.0f / len;
361                                 ldir[0] *= s;
362                                 ldir[1] *= s;
363                                 ldir[2] *= s;
364                         }
365                         falloff = 1.0f / (len * len);
366
367                         lcol[0] = lt->ob.emr * falloff;
368                         lcol[1] = lt->ob.emg * falloff;
369                         lcol[2] = lt->ob.emb * falloff;
370
371                         if((ndotl = hit->nx * ldir[0] + hit->ny * ldir[1] + hit->nz * ldir[2]) < 0.0f) {
372                                 ndotl = 0.0f;
373                         }
374
375                         dcol[0] += o->ob.r * lcol[0] * ndotl;
376                         dcol[1] += o->ob.g * lcol[1] * ndotl;
377                         dcol[2] += o->ob.b * lcol[2] * ndotl;
378
379                         if(o->ob.roughness < 1.0f) {
380                                 float gloss = 1.0f - o->ob.roughness;
381
382                                 hdir[0] = ldir[0] - ray->dx;
383                                 hdir[1] = ldir[1] - ray->dy;
384                                 hdir[2] = ldir[2] - ray->dz;
385                                 if((len = sqrt(hdir[0] * hdir[0] + hdir[1] * hdir[1] + hdir[2] * hdir[2])) != 0.0f) {
386                                         float s = 1.0f / len;
387                                         hdir[0] *= s;
388                                         hdir[1] *= s;
389                                         hdir[2] *= s;
390                                 }
391
392                                 if((ndoth = hit->nx * hdir[0] + hit->ny * hdir[1] + hit->nz * hdir[2]) < 0.0f) {
393                                         ndoth = 0.0f;
394                                 }
395                                 spec = gloss * pow(ndoth, 100.0f * gloss);
396
397                                 if(o->ob.metallic) {
398                                         lcol[0] *= o->ob.r;
399                                         lcol[1] *= o->ob.g;
400                                         lcol[2] *= o->ob.b;
401                                 }
402                                 scol[0] += lcol[0] * spec;
403                                 scol[1] += lcol[1] * spec;
404                                 scol[2] += lcol[2] * spec;
405                         }
406                 }
407
408                 lt = lt->ob.plt_next;
409         }
410
411         col[0] = dcol[0] + scol[0];
412         col[1] = dcol[1] + scol[1];
413         col[2] = dcol[2] + scol[2];
414 }
415
416 static void background(float *col, struct ray *ray)
417 {
418         col[0] = col[1] = col[2] = 0.0f;
419 }
420
421 static int find_intersection(struct ray *ray, struct hit *best)
422 {
423         int idx = 0;
424         csg_object *o;
425         struct hinterv *hit, *it;
426
427         best->t = FLT_MAX;
428         best->o = 0;
429
430         o = oblist;
431         while(o) {
432                 if((hit = ray_intersect(ray, o))) {
433                         it = hit;
434                         while(it) {
435                                 if(it->end[0].t > 1e-6) {
436                                         idx = 0;
437                                         break;
438                                 }
439                                 if(it->end[1].t > 1e-6) {
440                                         idx = 1;
441                                         break;
442                                 }
443                                 it = it->next;
444                         }
445
446                         if(it && it->end[idx].t < best->t) {
447                                 *best = it->end[idx];
448                         }
449                 }
450                 free_hit_list(hit);
451                 o = o->ob.next;
452         }
453
454         return best->o != 0;
455 }