first render
[retroray] / src / scene.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdlib.h>
19 #include <float.h>
20 #include "scene.h"
21 #include "geom.h"
22 #include "darray.h"
23 #include "logger.h"
24
25 static struct material *default_material(void);
26
27 struct scene *create_scene(void)
28 {
29         struct scene *scn;
30
31         if(!(scn = malloc(sizeof *scn))) {
32                 errormsg("failed to allocate scene\n");
33                 return 0;
34         }
35         scn->objects = darr_alloc(0, sizeof *scn->objects);
36         scn->lights = darr_alloc(0, sizeof *scn->lights);
37         scn->mtl = darr_alloc(0, sizeof *scn->mtl);
38         return scn;
39 }
40
41 void free_scene(struct scene *scn)
42 {
43         int i;
44
45         if(!scn) return;
46
47         for(i=0; i<darr_size(scn->objects); i++) {
48                 free_object(scn->objects[i]);
49         }
50         darr_free(scn->objects);
51
52         for(i=0; i<darr_size(scn->lights); i++) {
53                 free_light(scn->lights[i]);
54         }
55         darr_free(scn->lights);
56
57         for(i=0; i<darr_size(scn->mtl); i++) {
58                 mtl_destroy(scn->mtl[i]);
59                 free(scn->mtl[i]);
60         }
61         darr_free(scn->mtl);
62
63         free(scn);
64 }
65
66 int scn_add_object(struct scene *scn, struct object *obj)
67 {
68         darr_push(scn->objects, &obj);
69         return 0;
70 }
71
72 int scn_rm_object(struct scene *scn, int idx)
73 {
74         int numobj = darr_size(scn->objects);
75
76         if(idx < 0 || idx >= numobj) {
77                 return -1;
78         }
79
80         free_object(scn->objects[idx]);
81
82         if(idx < numobj - 1) {
83                 scn->objects[idx] = scn->objects[numobj - 1];
84         }
85         darr_pop(scn->objects);
86         return 0;
87 }
88
89 int scn_num_objects(const struct scene *scn)
90 {
91         return darr_size(scn->objects);
92 }
93
94 int scn_object_index(const struct scene *scn, const struct object *obj)
95 {
96         int i, num = darr_size(scn->objects);
97         for(i=0; i<num; i++) {
98                 if(scn->objects[i] == obj) {
99                         return i;
100                 }
101         }
102         return -1;
103 }
104
105
106
107 int scn_add_material(struct scene *scn, struct material *mtl)
108 {
109         darr_push(scn->mtl, &mtl);
110         return 0;
111 }
112
113 int scn_rm_material(struct scene *scn, struct material *mtl)
114 {
115         int idx, num_mtl;
116
117         if((idx = scn_material_index(scn, mtl)) == -1) {
118                 return -1;
119         }
120
121         num_mtl = darr_size(scn->mtl);
122
123         if(idx < num_mtl - 1) {
124                 scn->mtl[idx] = scn->mtl[num_mtl - 1];
125         }
126         darr_pop(scn->mtl);
127         return 0;
128 }
129
130 int scn_num_materials(const struct scene *scn)
131 {
132         return darr_size(scn->mtl);
133 }
134
135 int scn_material_index(const struct scene *scn, const struct material *mtl)
136 {
137         int i, num_mtl;
138
139         num_mtl = darr_size(scn->mtl);
140         for(i=0; i<num_mtl; i++) {
141                 if(scn->mtl[i] == mtl) {
142                         return i;
143                 }
144         }
145         return -1;
146 }
147
148 struct material *scn_find_material(const struct scene *scn, const char *mname)
149 {
150         int i, num_mtl;
151
152         num_mtl = darr_size(scn->mtl);
153         for(i=0; i<num_mtl; i++) {
154                 if(strcmp(scn->mtl[i]->name, mname) == 0) {
155                         return scn->mtl[i];
156                 }
157         }
158         return 0;
159 }
160
161 /* manage lights */
162
163 int scn_add_light(struct scene *scn, struct light *light)
164 {
165         darr_push(scn->lights, &light);
166         return 0;
167 }
168
169 int scn_rm_light(struct scene *scn, struct light *light)
170 {
171         int idx, num_lights;
172
173         if((idx = scn_light_index(scn, light)) == -1) {
174                 return -1;
175         }
176
177         num_lights = darr_size(scn->lights);
178
179         if(idx < num_lights - 1) {
180                 scn->lights[idx] = scn->lights[num_lights - 1];
181         }
182         darr_pop(scn->lights);
183         return 0;
184 }
185
186 int scn_num_lights(const struct scene *scn)
187 {
188         return darr_size(scn->lights);
189 }
190
191 int scn_light_index(const struct scene *scn, const struct light *light)
192 {
193         int i, num_lights;
194
195         num_lights = darr_size(scn->lights);
196         for(i=0; i<num_lights; i++) {
197                 if(scn->lights[i] == light) {
198                         return i;
199                 }
200         }
201         return -1;
202 }
203
204 struct light *scn_find_light(const struct scene *scn, const char *mname)
205 {
206         int i, num_lights;
207
208         num_lights = darr_size(scn->lights);
209         for(i=0; i<num_lights; i++) {
210                 if(strcmp(scn->lights[i]->name, mname) == 0) {
211                         return scn->lights[i];
212                 }
213         }
214         return 0;
215 }
216
217
218 int scn_intersect(const struct scene *scn, const cgm_ray *ray, struct rayhit *hit)
219 {
220         int i, numobj;
221         struct rayhit hit0, tmphit;
222
223         hit0.t = FLT_MAX;
224         hit0.obj = 0;
225
226         numobj = darr_size(scn->objects);
227         for(i=0; i<numobj; i++) {
228                 if(ray_object(ray, scn->objects[i], &tmphit) && tmphit.t < hit0.t) {
229                         hit0 = tmphit;
230                 }
231         }
232
233         if(hit0.obj) {
234                 if(hit) *hit = hit0;
235                 return 1;
236         }
237         return 0;
238 }
239
240
241 /* --- object functions --- */
242
243 struct object *create_object(int type)
244 {
245         struct object *obj;
246         struct sphere *sph;
247         char buf[32];
248         static int objid;
249
250         switch(type) {
251         case OBJ_SPHERE:
252                 if(!(obj = calloc(1, sizeof *sph))) {
253                         goto err;
254                 }
255                 sph = (struct sphere*)obj;
256                 sph->rad = 1.0f;
257                 sprintf(buf, "sphere%03d", objid);
258                 break;
259
260         default:
261                 if(!(obj = calloc(1, sizeof *obj))) {
262                         goto err;
263                 }
264                 sprintf(buf, "object%03d", objid);
265                 break;
266         }
267
268         obj->type = type;
269
270         cgm_vcons(&obj->pos, 0, 0, 0);
271         cgm_qcons(&obj->rot, 0, 0, 0, 1);
272         cgm_vcons(&obj->scale, 1, 1, 1);
273         cgm_vcons(&obj->pivot, 0, 0, 0);
274         cgm_midentity(obj->xform);
275         cgm_midentity(obj->inv_xform);
276         obj->xform_valid = 1;
277         obj->mtl = default_material();
278
279         set_object_name(obj, buf);
280         objid++;
281         return obj;
282
283 err:
284         errormsg("failed to allocate object\n");
285         return 0;
286 }
287
288 void free_object(struct object *obj)
289 {
290         if(!obj) return;
291
292         free(obj->name);
293         free(obj);
294 }
295
296 int set_object_name(struct object *obj, const char *name)
297 {
298         char *str = strdup(name);
299         if(!str) return -1;
300
301         free(obj->name);
302         obj->name = str;
303         return 0;
304 }
305
306 void calc_object_matrix(struct object *obj)
307 {
308         int i;
309         float rmat[16];
310         float *mat = obj->xform;
311
312         cgm_mtranslation(mat, obj->pivot.x, obj->pivot.y, obj->pivot.z);
313         cgm_mrotation_quat(rmat, &obj->rot);
314
315         for(i=0; i<3; i++) {
316                 mat[i] = rmat[i];
317                 mat[4 + i] = rmat[4 + i];
318                 mat[8 + i] = rmat[8 + i];
319         }
320
321         mat[0] *= obj->scale.x; mat[4] *= obj->scale.y; mat[8] *= obj->scale.z; mat[12] += obj->pos.x;
322         mat[1] *= obj->scale.x; mat[5] *= obj->scale.y; mat[9] *= obj->scale.z; mat[13] += obj->pos.y;
323         mat[2] *= obj->scale.x; mat[6] *= obj->scale.y; mat[10] *= obj->scale.z; mat[14] += obj->pos.z;
324
325         cgm_mpretranslate(mat, -obj->pivot.x, -obj->pivot.y, -obj->pivot.z);
326         /* that's basically: pivot * rotation * translation * scaling * -pivot */
327
328         cgm_mcopy(obj->inv_xform, mat);
329         cgm_minverse(obj->inv_xform);
330
331         obj->xform_valid = 1;
332 }
333
334 /* --- lights --- */
335 struct light *create_light(void)
336 {
337         struct light *lt;
338         static int ltidx;
339         char buf[64];
340
341         if(!(lt = malloc(sizeof *lt))) {
342                 return 0;
343         }
344         cgm_vcons(&lt->pos, 0, 0, 0);
345
346         set_light_color(lt, 1, 1, 1);
347         set_light_energy(lt, 1);
348
349         sprintf(buf, "light%03d", ltidx++);
350         set_light_name(lt, buf);
351
352         return lt;
353 }
354
355 void free_light(struct light *lt)
356 {
357         if(!lt) return;
358         free(lt->name);
359         free(lt);
360 }
361
362 int set_light_name(struct light *lt, const char *name)
363 {
364         char *tmp = strdup(name);
365         if(!tmp) return -1;
366         free(lt->name);
367         lt->name = tmp;
368         return 0;
369 }
370
371 void set_light_color(struct light *lt, float r, float g, float b)
372 {
373         cgm_vcons(&lt->orig_color, r, g, b);
374         lt->color = lt->orig_color;
375         cgm_vscale(&lt->color, lt->energy);
376 }
377
378 void set_light_energy(struct light *lt, float e)
379 {
380         lt->energy = e;
381         lt->color = lt->orig_color;
382         cgm_vscale(&lt->color, e);
383 }
384
385 static struct material *default_material(void)
386 {
387         static struct material defmtl;
388
389         if(!defmtl.name) {
390                 mtl_init(&defmtl);
391                 mtl_set_name(&defmtl, "default_mtl");
392         }
393         return &defmtl;
394 }