47f2a8b25ad33c416d4d5700f0c9becc034239e9
[csgray] / src / csgray.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include "csgimpl.h"
6 #include "matrix.h"
7 #include "geom.h"
8
9 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect);
10 static int ray_trace(struct ray *ray, float *col);
11 static void shade(float *col, struct ray *ray, struct hit *hit);
12 static void background(float *col, struct ray *ray);
13 static int find_intersection(struct ray *ray, struct hit *best);
14
15 static struct camera cam;
16 static csg_object *oblist;
17
18 int csg_init(void)
19 {
20         oblist = 0;
21         csg_view(0, 0, 5, 0, 0, 0);
22         csg_fov(50);
23
24         return 0;
25 }
26
27 void csg_destroy(void)
28 {
29         while(oblist) {
30                 csg_object *o = oblist;
31                 oblist = oblist->ob.next;
32                 csg_free_object(o);
33         }
34         oblist = 0;
35 }
36
37 void csg_view(float x, float y, float z, float tx, float ty, float tz)
38 {
39         cam.x = x;
40         cam.y = y;
41         cam.z = z;
42         cam.tx = tx;
43         cam.ty = ty;
44         cam.tz = tz;
45 }
46
47 void csg_fov(float fov)
48 {
49         cam.fov = M_PI * fov / 180.0f;
50 }
51
52
53 int csg_load(const char *fname)
54 {
55         return 0;       /* TODO */
56 }
57
58 int csg_save(const char *fname)
59 {
60         return 0;       /* TODO */
61 }
62
63 void csg_add_object(csg_object *o)
64 {
65         o->ob.next = oblist;
66         oblist = o;
67 }
68
69 int csg_remove_object(csg_object *o)
70 {
71         csg_object dummy, *n;
72
73         dummy.ob.next = oblist;
74         n = &dummy;
75
76         while(n->ob.next) {
77                 if(n->ob.next == o) {
78                         n->ob.next = o->ob.next;
79                         return 1;
80                 }
81                 n = n->ob.next;
82         }
83         return 0;
84 }
85
86 void csg_free_object(csg_object *o)
87 {
88         if(o->ob.destroy) {
89                 o->ob.destroy(o);
90         }
91         free(o);
92 }
93
94 static union csg_object *alloc_object(int type)
95 {
96         csg_object *o;
97
98         if(!(o = calloc(sizeof *o, 1))) {
99                 return 0;
100         }
101         o->ob.type = type;
102         mat4_identity(o->ob.xform);
103         mat4_identity(o->ob.inv_xform);
104
105         csg_emission(o, 0, 0, 0);
106         csg_color(o, 1, 1, 1);
107         csg_roughness(o, 1);
108         csg_opacity(o, 1);
109
110         return o;
111 }
112
113 csg_object *csg_null(float x, float y, float z)
114 {
115         return alloc_object(OB_NULL);
116 }
117
118 csg_object *csg_sphere(float x, float y, float z, float r)
119 {
120         csg_object *o;
121
122         if(!(o = alloc_object(OB_SPHERE))) {
123                 return 0;
124         }
125
126         o->sph.rad = r;
127         mat4_translation(o->ob.xform, x, y, z);
128         mat4_copy(o->ob.inv_xform, o->ob.xform);
129         mat4_inverse(o->ob.inv_xform);
130         return o;
131 }
132
133 csg_object *csg_cylinder(float x0, float y0, float z0, float x1, float y1, float z1, float r)
134 {
135         csg_object *o;
136         float dx, dy, dz;
137         int major;
138
139         if(!(o = alloc_object(OB_CYLINDER))) {
140                 return 0;
141         }
142
143         dx = x1 - x0;
144         dy = y1 - y0;
145         dz = z1 - z0;
146
147         if(fabs(dx) > fabs(dy) && fabs(dx) > fabs(dz)) {
148                 major = 0;
149         } else if(fabs(dy) > fabs(dz)) {
150                 major = 1;
151         } else {
152                 major = 2;
153         }
154
155         o->cyl.rad = r;
156         mat4_lookat(o->ob.xform, x0, y0, z0, x1, y1, z1, 0, major == 2 ? 1 : 0, major == 2 ? 0 : 1);
157         mat4_copy(o->ob.inv_xform, o->ob.xform);
158         mat4_inverse(o->ob.inv_xform);
159         return o;
160 }
161
162 csg_object *csg_plane(float x, float y, float z, float nx, float ny, float nz)
163 {
164         csg_object *o;
165         float len;
166
167         if(!(o = alloc_object(OB_PLANE))) {
168                 return 0;
169         }
170
171         len = sqrt(nx * nx + ny * ny + nz * nz);
172         if(len != 0.0f) {
173                 float s = 1.0f / len;
174                 nx *= s;
175                 ny *= s;
176                 nz *= s;
177         }
178
179         o->plane.nx = nx;
180         o->plane.ny = ny;
181         o->plane.nz = nz;
182         o->plane.d = x * nx + y * ny + z * nz;
183         return 0;
184 }
185
186 csg_object *csg_box(float x, float y, float z, float xsz, float ysz, float zsz)
187 {
188         return 0;
189 }
190
191 csg_object *csg_union(csg_object *a, csg_object *b)
192 {
193         csg_object *o;
194
195         if(!(o = alloc_object(OB_UNION))) {
196                 return 0;
197         }
198         o->un.a = a;
199         o->un.b = b;
200         return o;
201 }
202
203 csg_object *csg_intersection(csg_object *a, csg_object *b)
204 {
205         csg_object *o;
206
207         if(!(o = alloc_object(OB_INTERSECTION))) {
208                 return 0;
209         }
210         o->isect.a = a;
211         o->isect.b = b;
212         return o;
213 }
214
215 csg_object *csg_subtraction(csg_object *a, csg_object *b)
216 {
217         csg_object *o;
218
219         if(!(o = alloc_object(OB_SUBTRACTION))) {
220                 return 0;
221         }
222         o->sub.a = a;
223         o->sub.b = b;
224         return o;
225 }
226
227
228 void csg_emission(csg_object *o, float r, float g, float b)
229 {
230         o->ob.emr = r;
231         o->ob.emg = g;
232         o->ob.emb = b;
233 }
234
235 void csg_color(csg_object *o, float r, float g, float b)
236 {
237         o->ob.r = r;
238         o->ob.g = g;
239         o->ob.b = b;
240 }
241
242 void csg_roughness(csg_object *o, float r)
243 {
244         o->ob.roughness = r;
245 }
246
247 void csg_opacity(csg_object *o, float p)
248 {
249         o->ob.opacity = p;
250 }
251
252
253 void csg_render_pixel(int x, int y, int width, int height, float aspect, float *color)
254 {
255         struct ray ray;
256
257         calc_primary_ray(&ray, x, y, width, height, aspect);
258         ray_trace(&ray, color);
259 }
260
261 void csg_render_image(float *pixels, int width, int height)
262 {
263         int i, j;
264         float aspect = (float)width / (float)height;
265
266         for(i=0; i<height; i++) {
267                 for(j=0; j<width; j++) {
268                         csg_render_pixel(j, i, width, height, aspect, pixels);
269                         pixels += 3;
270                 }
271         }
272 }
273
274 static void calc_primary_ray(struct ray *ray, int x, int y, int w, int h, float aspect)
275 {
276         float px, py;
277
278         px = aspect * ((float)x / (float)w * 2.0f - 1.0f);
279         py = 1.0f - (float)y / (float)h * 2.0f;
280
281         ray->x = px;
282         ray->y = py;
283         ray->z = cam.z;
284
285         ray->dx = ray->dy = 0.0f;
286         ray->dz = -1.0f;
287 }
288
289 static int ray_trace(struct ray *ray, float *col)
290 {
291         struct hit hit;
292
293         if(!find_intersection(ray, &hit)) {
294                 background(col, ray);
295                 return 0;
296         }
297
298         shade(col, ray, &hit);
299         return 1;
300 }
301
302 static void shade(float *col, struct ray *ray, struct hit *hit)
303 {
304         col[0] = 1.0f;
305         col[1] = col[2] = 0.0f;
306 }
307
308 static void background(float *col, struct ray *ray)
309 {
310         col[0] = col[1] = col[2] = 0.0f;
311 }
312
313 static int find_intersection(struct ray *ray, struct hit *best)
314 {
315         csg_object *o;
316         struct hit *hit;
317
318         best->t = 1e-6f;
319         best->o = 0;
320
321         o = oblist;
322         while(o) {
323                 if((hit = ray_intersect(ray, o)) && hit->t < best->t) {
324                         *best = *hit;
325                 }
326                 free_hit(hit);
327                 o = o->ob.next;
328         }
329
330         return best->o != 0;
331 }