stop appending repeat keys to the input buffer
[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         if(!scn) return;
44
45         scn_clear(scn);
46
47         darr_free(scn->objects);
48         darr_free(scn->lights);
49         darr_free(scn->mtl);
50         free(scn);
51 }
52
53 void scn_clear(struct scene *scn)
54 {
55         int i;
56
57         for(i=0; i<darr_size(scn->objects); i++) {
58                 free_object(scn->objects[i]);
59         }
60         darr_clear(scn->objects);
61
62         for(i=0; i<darr_size(scn->lights); i++) {
63                 free_light(scn->lights[i]);
64         }
65         darr_clear(scn->lights);
66
67         for(i=0; i<darr_size(scn->mtl); i++) {
68                 mtl_destroy(scn->mtl[i]);
69                 free(scn->mtl[i]);
70         }
71         darr_clear(scn->mtl);
72 }
73
74 int scn_add_object(struct scene *scn, struct object *obj)
75 {
76         darr_push(scn->objects, &obj);
77         return 0;
78 }
79
80 int scn_rm_object(struct scene *scn, int idx)
81 {
82         int numobj = darr_size(scn->objects);
83
84         if(idx < 0 || idx >= numobj) {
85                 return -1;
86         }
87
88         free_object(scn->objects[idx]);
89
90         if(idx < numobj - 1) {
91                 scn->objects[idx] = scn->objects[numobj - 1];
92         }
93         darr_pop(scn->objects);
94         return 0;
95 }
96
97 int scn_num_objects(const struct scene *scn)
98 {
99         return darr_size(scn->objects);
100 }
101
102 int scn_object_index(const struct scene *scn, const struct object *obj)
103 {
104         int i, num = darr_size(scn->objects);
105         for(i=0; i<num; i++) {
106                 if(scn->objects[i] == obj) {
107                         return i;
108                 }
109         }
110         return -1;
111 }
112
113
114
115 int scn_add_material(struct scene *scn, struct material *mtl)
116 {
117         darr_push(scn->mtl, &mtl);
118         return 0;
119 }
120
121 int scn_rm_material(struct scene *scn, struct material *mtl)
122 {
123         int idx, num_mtl;
124
125         if((idx = scn_material_index(scn, mtl)) == -1) {
126                 return -1;
127         }
128
129         num_mtl = darr_size(scn->mtl);
130
131         if(idx < num_mtl - 1) {
132                 scn->mtl[idx] = scn->mtl[num_mtl - 1];
133         }
134         darr_pop(scn->mtl);
135         return 0;
136 }
137
138 int scn_num_materials(const struct scene *scn)
139 {
140         return darr_size(scn->mtl);
141 }
142
143 int scn_material_index(const struct scene *scn, const struct material *mtl)
144 {
145         int i, num_mtl;
146
147         num_mtl = darr_size(scn->mtl);
148         for(i=0; i<num_mtl; i++) {
149                 if(scn->mtl[i] == mtl) {
150                         return i;
151                 }
152         }
153         return -1;
154 }
155
156 struct material *scn_find_material(const struct scene *scn, const char *mname)
157 {
158         int i, num_mtl;
159
160         num_mtl = darr_size(scn->mtl);
161         for(i=0; i<num_mtl; i++) {
162                 if(strcmp(scn->mtl[i]->name, mname) == 0) {
163                         return scn->mtl[i];
164                 }
165         }
166         return 0;
167 }
168
169 /* manage lights */
170
171 int scn_add_light(struct scene *scn, struct light *light)
172 {
173         darr_push(scn->lights, &light);
174         return 0;
175 }
176
177 int scn_rm_light(struct scene *scn, struct light *light)
178 {
179         int idx, num_lights;
180
181         if((idx = scn_light_index(scn, light)) == -1) {
182                 return -1;
183         }
184
185         num_lights = darr_size(scn->lights);
186
187         if(idx < num_lights - 1) {
188                 scn->lights[idx] = scn->lights[num_lights - 1];
189         }
190         darr_pop(scn->lights);
191         return 0;
192 }
193
194 int scn_num_lights(const struct scene *scn)
195 {
196         return darr_size(scn->lights);
197 }
198
199 int scn_light_index(const struct scene *scn, const struct light *light)
200 {
201         int i, num_lights;
202
203         num_lights = darr_size(scn->lights);
204         for(i=0; i<num_lights; i++) {
205                 if(scn->lights[i] == light) {
206                         return i;
207                 }
208         }
209         return -1;
210 }
211
212 struct light *scn_find_light(const struct scene *scn, const char *mname)
213 {
214         int i, num_lights;
215
216         num_lights = darr_size(scn->lights);
217         for(i=0; i<num_lights; i++) {
218                 if(strcmp(scn->lights[i]->name, mname) == 0) {
219                         return scn->lights[i];
220                 }
221         }
222         return 0;
223 }
224
225
226 int scn_intersect(const struct scene *scn, const cgm_ray *ray, struct rayhit *hit)
227 {
228         int i, numobj;
229         struct rayhit hit0, tmphit;
230
231         hit0.t = FLT_MAX;
232         hit0.obj = 0;
233
234         numobj = darr_size(scn->objects);
235         for(i=0; i<numobj; i++) {
236                 if(ray_object(ray, scn->objects[i], &tmphit) && tmphit.t < hit0.t) {
237                         hit0 = tmphit;
238                 }
239         }
240
241         if(hit0.obj) {
242                 if(hit) *hit = hit0;
243                 return 1;
244         }
245         return 0;
246 }
247
248
249 /* --- object functions --- */
250
251 struct object *create_object(int type)
252 {
253         struct object *obj;
254         struct sphere *sph;
255         char buf[32];
256         static int objid;
257
258         switch(type) {
259         case OBJ_SPHERE:
260                 if(!(obj = calloc(1, sizeof *sph))) {
261                         goto err;
262                 }
263                 sph = (struct sphere*)obj;
264                 sph->rad = 1.0f;
265                 sprintf(buf, "sphere%03d", objid);
266                 break;
267
268         default:
269                 if(!(obj = calloc(1, sizeof *obj))) {
270                         goto err;
271                 }
272                 sprintf(buf, "object%03d", objid);
273                 break;
274         }
275
276         obj->type = type;
277
278         cgm_vcons(&obj->pos, 0, 0, 0);
279         cgm_qcons(&obj->rot, 0, 0, 0, 1);
280         cgm_vcons(&obj->scale, 1, 1, 1);
281         cgm_vcons(&obj->pivot, 0, 0, 0);
282         cgm_midentity(obj->xform);
283         cgm_midentity(obj->inv_xform);
284         obj->xform_valid = 1;
285         obj->mtl = default_material();
286
287         set_object_name(obj, buf);
288         objid++;
289         return obj;
290
291 err:
292         errormsg("failed to allocate object\n");
293         return 0;
294 }
295
296 void free_object(struct object *obj)
297 {
298         if(!obj) return;
299
300         free(obj->name);
301         free(obj);
302 }
303
304 int set_object_name(struct object *obj, const char *name)
305 {
306         char *str = strdup(name);
307         if(!str) return -1;
308
309         free(obj->name);
310         obj->name = str;
311         return 0;
312 }
313
314 void calc_object_matrix(struct object *obj)
315 {
316         int i;
317         float rmat[16];
318         float *mat = obj->xform;
319
320         cgm_mtranslation(mat, obj->pivot.x, obj->pivot.y, obj->pivot.z);
321         cgm_mrotation_quat(rmat, &obj->rot);
322
323         for(i=0; i<3; i++) {
324                 mat[i] = rmat[i];
325                 mat[4 + i] = rmat[4 + i];
326                 mat[8 + i] = rmat[8 + i];
327         }
328
329         mat[0] *= obj->scale.x; mat[4] *= obj->scale.y; mat[8] *= obj->scale.z; mat[12] += obj->pos.x;
330         mat[1] *= obj->scale.x; mat[5] *= obj->scale.y; mat[9] *= obj->scale.z; mat[13] += obj->pos.y;
331         mat[2] *= obj->scale.x; mat[6] *= obj->scale.y; mat[10] *= obj->scale.z; mat[14] += obj->pos.z;
332
333         cgm_mpretranslate(mat, -obj->pivot.x, -obj->pivot.y, -obj->pivot.z);
334         /* that's basically: pivot * rotation * translation * scaling * -pivot */
335
336         cgm_mcopy(obj->inv_xform, mat);
337         cgm_minverse(obj->inv_xform);
338
339         obj->xform_valid = 1;
340 }
341
342 /* --- lights --- */
343 struct light *create_light(void)
344 {
345         struct light *lt;
346         static int ltidx;
347         char buf[64];
348
349         if(!(lt = malloc(sizeof *lt))) {
350                 return 0;
351         }
352         cgm_vcons(&lt->pos, 0, 0, 0);
353
354         set_light_color(lt, 1, 1, 1);
355         set_light_energy(lt, 1);
356
357         sprintf(buf, "light%03d", ltidx++);
358         set_light_name(lt, buf);
359
360         return lt;
361 }
362
363 void free_light(struct light *lt)
364 {
365         if(!lt) return;
366         free(lt->name);
367         free(lt);
368 }
369
370 int set_light_name(struct light *lt, const char *name)
371 {
372         char *tmp = strdup(name);
373         if(!tmp) return -1;
374         free(lt->name);
375         lt->name = tmp;
376         return 0;
377 }
378
379 void set_light_color(struct light *lt, float r, float g, float b)
380 {
381         cgm_vcons(&lt->orig_color, r, g, b);
382         lt->color = lt->orig_color;
383         cgm_vscale(&lt->color, lt->energy);
384 }
385
386 void set_light_energy(struct light *lt, float e)
387 {
388         lt->energy = e;
389         lt->color = lt->orig_color;
390         cgm_vscale(&lt->color, e);
391 }
392
393 static struct material *default_material(void)
394 {
395         static struct material defmtl;
396
397         if(!defmtl.name) {
398                 mtl_init(&defmtl);
399                 mtl_set_name(&defmtl, "default_mtl");
400         }
401         return &defmtl;
402 }