53b01468d021feb2a52c32087b742fc0dcb08d7f
[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.05, 0.05, 0.05);
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 0;
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
277 void csg_render_pixel(int x, int y, int width, int height, float aspect, float *color)
278 {
279         struct ray ray;
280
281         calc_primary_ray(&ray, x, y, width, height, aspect);
282         ray_trace(&ray, color);
283 }
284
285 void csg_render_image(float *pixels, int width, int height)
286 {
287         int i, j;
288         float aspect = (float)width / (float)height;
289
290         for(i=0; i<height; i++) {
291                 for(j=0; j<width; j++) {
292                         csg_render_pixel(j, i, width, height, aspect, pixels);
293                         pixels += 3;
294                 }
295         }
296 }
297
298 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect)
299 {
300         /* TODO */
301         ray->dx = aspect * ((float)x / (float)w * 2.0f - 1.0f);
302         ray->dy = 1.0f - (float)y / (float)h * 2.0f;
303         ray->dz = -1.0f / tan(cam.fov * 0.5f);
304
305         ray->x = cam.x;
306         ray->y = cam.y;
307         ray->z = cam.z;
308 }
309
310 static int ray_trace(struct ray *ray, float *col)
311 {
312         struct hit hit;
313
314         if(!find_intersection(ray, &hit)) {
315                 background(col, ray);
316                 return 0;
317         }
318
319         shade(col, ray, &hit);
320         return 1;
321 }
322
323 #define NULLXPOS(o)     ((o)->ob.xform[12])
324 #define NULLYPOS(o)     ((o)->ob.xform[13])
325 #define NULLZPOS(o)     ((o)->ob.xform[14])
326
327 static void shade(float *col, struct ray *ray, struct hit *hit)
328 {
329         float ndotl, len;
330         csg_object *o, *lt = plights;
331         float dcol[3], scol[3] = {0};
332         float ldir[3];
333         struct ray sray;
334         struct hit tmphit;
335
336         o = hit->o;
337         dcol[0] = ambient[0];
338         dcol[1] = ambient[1];
339         dcol[2] = ambient[2];
340
341         while(lt) {
342                 ldir[0] = NULLXPOS(lt) - hit->x;
343                 ldir[1] = NULLYPOS(lt) - hit->y;
344                 ldir[2] = NULLZPOS(lt) - hit->z;
345
346                 sray.x = hit->x;
347                 sray.y = hit->y;
348                 sray.z = hit->z;
349                 sray.dx = ldir[0];
350                 sray.dy = ldir[1];
351                 sray.dz = ldir[2];
352
353                 if(!find_intersection(&sray, &tmphit) || tmphit.t < 1.0f) {
354                         if((len = sqrt(ldir[0] * ldir[0] + ldir[1] * ldir[1] + ldir[2] * ldir[2])) != 0.0f) {
355                                 float s = 1.0f / len;
356                                 ldir[0] *= s;
357                                 ldir[1] *= s;
358                                 ldir[2] *= s;
359                         }
360
361                         if((ndotl = hit->nx * ldir[0] + hit->ny * ldir[1] + hit->nz * ldir[2]) < 0.0f) {
362                                 ndotl = 0.0f;
363                         }
364
365                         dcol[0] += o->ob.r * lt->ob.emr * ndotl;
366                         dcol[1] += o->ob.g * lt->ob.emg * ndotl;
367                         dcol[2] += o->ob.b * lt->ob.emb * ndotl;
368                 }
369
370                 lt = lt->ob.next;
371         }
372
373         col[0] = dcol[0] + scol[0];
374         col[1] = dcol[1] + scol[1];
375         col[2] = dcol[2] + scol[2];
376 }
377
378 static void background(float *col, struct ray *ray)
379 {
380         col[0] = col[1] = col[2] = 0.0f;
381 }
382
383 static int find_intersection(struct ray *ray, struct hit *best)
384 {
385         csg_object *o;
386         struct hit *hit;
387
388         best->t = FLT_MAX;
389         best->o = 0;
390
391         o = oblist;
392         while(o) {
393                 if((hit = ray_intersect(ray, o)) && hit->t < best->t) {
394                         *best = *hit;
395                 }
396                 free_hit_list(hit);
397                 o = o->ob.next;
398         }
399
400         return best->o != 0;
401 }