removed clang-format and clang_complete files from the repo
[dosdemo] / src / scr / rt.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <float.h>
5 #include "rt.h"
6 #include "util.h"
7 #include "darray.h"
8
9 static cgm_vec3 cur_col, cur_spec;
10 static float cur_shin;
11 static struct image *cur_tex;
12
13 void rt_init(struct rtscene *scn)
14 {
15         scn->obj = darr_alloc(0, sizeof *scn->obj);
16         scn->num_obj = 0;
17         scn->lt = darr_alloc(0, sizeof *scn->lt);
18         scn->num_lt = 0;
19
20         cgm_vcons(&cur_col, 1, 1, 1);
21         cgm_vcons(&cur_spec, 0, 0, 0);
22         cur_shin = 1;
23         cur_tex = 0;
24 }
25
26 void rt_destroy(struct rtscene *scn)
27 {
28         darr_free(scn->obj);
29         darr_free(scn->lt);
30         memset(scn, 0, sizeof *scn);
31 }
32
33
34 void rt_color(float r, float g, float b)
35 {
36         cgm_vcons(&cur_col, r, g, b);
37 }
38
39 void rt_specular(float r, float g, float b)
40 {
41         cgm_vcons(&cur_spec, r, g, b);
42 }
43
44 void rt_shininess(float s)
45 {
46         cur_shin = s;
47 }
48
49 static union rtobject *add_object(struct rtscene *scn, enum rt_obj_type type)
50 {
51         union rtobject *obj;
52
53         obj = calloc_nf(1, sizeof *obj);
54         obj->type = type;
55
56         obj->x.mtl.kd = cur_col;
57         obj->x.mtl.ks = cur_spec;
58         obj->x.mtl.shin = cur_shin;
59         obj->x.mtl.tex = cur_tex;
60
61         darr_push(scn->obj, &obj);
62         scn->num_obj = darr_size(scn->obj);
63         return obj;
64 }
65
66 union rtobject *rt_add_sphere(struct rtscene *scn, float x, float y, float z, float r)
67 {
68         union rtobject *obj = add_object(scn, RT_SPH);
69         cgm_vcons(&obj->s.p, x, y, z);
70         obj->s.r = r;
71         return obj;
72 }
73
74 union rtobject *rt_add_plane(struct rtscene *scn, float nx, float ny, float nz, float d)
75 {
76         union rtobject *obj = add_object(scn, RT_PLANE);
77         cgm_vcons(&obj->p.n, nx, ny, nz);
78         obj->p.d = d;
79         return obj;
80 }
81
82 struct rtlight *rt_add_light(struct rtscene *scn, float x, float y, float z)
83 {
84         struct rtlight *lt = calloc_nf(1, sizeof *lt);
85
86         cgm_vcons(&lt->p, x, y, z);
87         lt->color = cur_col;
88
89         darr_push(scn->lt, &lt);
90         scn->num_lt = darr_size(scn->lt);
91         return lt;
92 }
93
94
95 /* color is initialized to black */
96 static void shade(struct rayhit *hit, struct rtscene *scn, int lvl, cgm_vec3 *color)
97 {
98         int i;
99         float ndotl, vdotr, spec;
100         cgm_ray sray;
101         cgm_vec3 col, rdir;
102         struct rtlight *lt;
103         struct rtmaterial *mtl = &hit->obj->x.mtl;
104
105         sray.origin = hit->p;
106         cgm_vnormalize(&hit->n);
107         cgm_vnormalize(&hit->ray->dir);
108
109         for(i=0; i<scn->num_lt; i++) {
110                 lt = scn->lt[i];
111                 sray.dir = lt->p;
112                 cgm_vsub(&sray.dir, &sray.origin);
113
114                 if(ray_scene(&sray, scn, 1.0f, 0)) continue;
115
116                 cgm_vnormalize(&sray.dir);
117                 ndotl = cgm_vdot(&sray.dir, &hit->n);
118                 if(ndotl < 0.0f) ndotl = 0.0f;
119
120                 rdir = hit->ray->dir;
121                 cgm_vreflect(&rdir, &hit->n);
122                 vdotr = cgm_vdot(&sray.dir, &rdir);
123                 if(vdotr < 0.0f) vdotr = 0.0f;
124                 spec = pow(vdotr, mtl->shin);
125
126                 color->x += (mtl->kd.x * ndotl + mtl->ks.x * spec) * lt->color.x;
127                 color->y += (mtl->kd.y * ndotl + mtl->ks.y * spec) * lt->color.y;
128                 color->z += (mtl->kd.z * ndotl + mtl->ks.z * spec) * lt->color.z;
129         }
130 }
131
132 int ray_trace(cgm_ray *ray, struct rtscene *scn, int lvl, cgm_vec3 *color)
133 {
134         struct rayhit hit;
135
136         color->x = color->y = color->z = 0.0f;
137         if(!ray_scene(ray, scn, FLT_MAX, &hit)) {
138                 return 0;
139         }
140         hit.ray = ray;
141         shade(&hit, scn, lvl, color);
142         return 1;
143 }
144
145
146 int ray_scene(cgm_ray *ray, struct rtscene *scn, float maxt, struct rayhit *hit)
147 {
148         int i;
149
150         if(hit) {
151                 struct rayhit hit0 = {FLT_MAX};
152
153                 /* find nearest hit */
154                 for(i=0; i<scn->num_obj; i++) {
155                         if(ray_object(ray, scn->obj[i], maxt, hit) && hit->t < hit0.t) {
156                                 hit0 = *hit;
157                         }
158                 }
159
160                 if(hit0.obj) {
161                         *hit = hit0;
162                         return 1;
163                 }
164         } else {
165                 /* find any hit */
166                 for(i=0; i<scn->num_obj; i++) {
167                         if(ray_object(ray, scn->obj[i], maxt, 0)) {
168                                 return 1;
169                         }
170                 }
171         }
172
173         return 0;
174 }
175
176 int ray_object(cgm_ray *ray, union rtobject *obj, float maxt, struct rayhit *hit)
177 {
178         switch(obj->type) {
179         case RT_SPH:
180                 return ray_sphere(ray, &obj->s, maxt, hit);
181         case RT_PLANE:
182                 return ray_plane(ray, &obj->p, maxt, hit);
183         default:
184                 break;
185         }
186         return 0;
187 }
188
189 #define SQ(x)   ((x) * (x))
190 int ray_sphere(cgm_ray *ray, struct rtsphere *sph, float maxt, struct rayhit *hit)
191 {
192         float a, a2, b, c, d, sqrt_d, t1, t2;
193
194         a = SQ(ray->dir.x) + SQ(ray->dir.y) + SQ(ray->dir.z);
195         b = 2.0f * ray->dir.x * (ray->origin.x - sph->p.x) +
196                 2.0f * ray->dir.y * (ray->origin.y - sph->p.y) +
197                 2.0f * ray->dir.z * (ray->origin.z - sph->p.z);
198         c = SQ(sph->p.x) + SQ(sph->p.y) + SQ(sph->p.z) +
199                 SQ(ray->origin.x) + SQ(ray->origin.y) + SQ(ray->origin.z) +
200                 2.0f * (-sph->p.x * ray->origin.x - sph->p.y * ray->origin.y - sph->p.z * ray->origin.z) -
201                 SQ(sph->r);
202
203         if((d = SQ(b) - 4.0f * a * c) < 0.0f) return 0;
204
205         sqrt_d = sqrt(d);
206         a2 = 2.0f * a;
207         t1 = (-b + sqrt_d) / a2;
208         t2 = (-b - sqrt_d) / a2;
209
210         if((t1 < 1e-5f && t2 < 1e-5f) || (t1 > maxt && t2 > maxt)) {
211                 return 0;
212         }
213
214         if(hit) {
215                 float t;
216                 if(t1 < 1e-5f) {
217                         t = t2;
218                 } else if(t2 < 1e-5f) {
219                         t = t1;
220                 } else {
221                         t = t1 < t2 ? t1 : t2;
222                 }
223
224                 hit->t = t;
225                 cgm_raypos(&hit->p, ray, t);
226
227                 hit->n.x = hit->p.x - sph->p.x;
228                 hit->n.y = hit->p.y - sph->p.y;
229                 hit->n.z = hit->p.z - sph->p.z;
230
231                 hit->obj = (union rtobject*)sph;
232         }
233         return 1;
234 }
235
236 int ray_plane(cgm_ray *ray, struct rtplane *plane, float maxt, struct rayhit *hit)
237 {
238         cgm_vec3 vo;
239         float t, ndotdir;
240
241         ndotdir = cgm_vdot(&plane->n, &ray->dir);
242         if(fabs(ndotdir) < 1e-5) {
243                 return 0;
244         }
245
246         vo.x = plane->n.x * plane->d - ray->origin.x;
247         vo.y = plane->n.y * plane->d - ray->origin.y;
248         vo.z = plane->n.z * plane->d - ray->origin.z;
249         t = cgm_vdot(&plane->n, &vo) / ndotdir;
250
251         if(t < 1e-5 || t > maxt) return 0;
252
253         if(hit) {
254                 hit->t = t;
255                 cgm_raypos(&hit->p, ray, t);
256                 hit->n = plane->n;
257
258                 hit->obj = (union rtobject*)plane;
259         }
260         return 1;
261 }