0ac2e71018f871957d901c4d2cd25f523e9aaf04
[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 "rt.h"
22 #include "darray.h"
23 #include "logger.h"
24
25 struct scene *create_scene(void)
26 {
27         struct scene *scn;
28
29         if(!(scn = malloc(sizeof *scn))) {
30                 errormsg("failed to allocate scene\n");
31                 return 0;
32         }
33         scn->objects = darr_alloc(0, sizeof *scn->objects);
34
35         return scn;
36 }
37
38 void free_scene(struct scene *scn)
39 {
40         int i;
41
42         if(!scn) return;
43
44         for(i=0; i<darr_size(scn->objects); i++) {
45                 free_object(scn->objects[i]);
46         }
47         darr_free(scn->objects);
48         free(scn);
49 }
50
51 int scn_add_object(struct scene *scn, struct object *obj)
52 {
53         darr_push(scn->objects, &obj);
54         return 0;
55 }
56
57 int scn_rm_object(struct scene *scn, int idx)
58 {
59         int numobj = darr_size(scn->objects);
60
61         if(idx < 0 || idx >= numobj) {
62                 return -1;
63         }
64
65         free_object(scn->objects[idx]);
66
67         if(idx < numobj - 1) {
68                 scn->objects[idx] = scn->objects[numobj - 1];
69         }
70         darr_pop(scn->objects);
71         return 0;
72 }
73
74 int scn_num_objects(const struct scene *scn)
75 {
76         return darr_size(scn->objects);
77 }
78
79 int scn_object_index(const struct scene *scn, const struct object *obj)
80 {
81         int i, num = darr_size(scn->objects);
82         for(i=0; i<num; i++) {
83                 if(scn->objects[i] == obj) {
84                         return i;
85                 }
86         }
87         return -1;
88 }
89
90 int scn_intersect(const struct scene *scn, const cgm_ray *ray, struct rayhit *hit)
91 {
92         int i, numobj;
93         struct rayhit hit0, tmphit;
94
95         hit0.t = FLT_MAX;
96         hit0.obj = 0;
97
98         numobj = darr_size(scn->objects);
99         for(i=0; i<numobj; i++) {
100                 if(ray_object(ray, scn->objects[i], &tmphit) && tmphit.t < hit0.t) {
101                         hit0 = tmphit;
102                 }
103         }
104
105         if(hit0.obj) {
106                 if(hit) *hit = hit0;
107                 return 1;
108         }
109         return 0;
110 }
111
112 struct object *create_object(int type)
113 {
114         struct object *obj;
115         struct sphere *sph;
116         char buf[32];
117         static int objid;
118
119         switch(type) {
120         case OBJ_SPHERE:
121                 if(!(obj = calloc(1, sizeof *sph))) {
122                         goto err;
123                 }
124                 sph = (struct sphere*)obj;
125                 sph->rad = 1.0f;
126                 sprintf(buf, "sphere%03d", objid);
127                 break;
128
129         default:
130                 if(!(obj = calloc(1, sizeof *obj))) {
131                         goto err;
132                 }
133                 sprintf(buf, "object%03d", objid);
134                 break;
135         }
136
137         obj->type = type;
138
139         cgm_vcons(&obj->pos, 0, 0, 0);
140         cgm_qcons(&obj->rot, 0, 0, 0, 1);
141         cgm_vcons(&obj->scale, 1, 1, 1);
142         cgm_vcons(&obj->pivot, 0, 0, 0);
143         cgm_midentity(obj->xform);
144         cgm_midentity(obj->inv_xform);
145         obj->xform_valid = 1;
146
147         set_object_name(obj, buf);
148         objid++;
149         return obj;
150
151 err:
152         errormsg("failed to allocate object\n");
153         return 0;
154 }
155
156 void free_object(struct object *obj)
157 {
158         if(!obj) return;
159
160         free(obj->name);
161         free(obj);
162 }
163
164 int set_object_name(struct object *obj, const char *name)
165 {
166         char *str = strdup(name);
167         if(!str) return -1;
168
169         free(obj->name);
170         obj->name = str;
171         return 0;
172 }
173
174 void calc_object_matrix(struct object *obj)
175 {
176         int i;
177         float rmat[16];
178         float *mat = obj->xform;
179
180         cgm_mtranslation(mat, obj->pivot.x, obj->pivot.y, obj->pivot.z);
181         cgm_mrotation_quat(rmat, &obj->rot);
182
183         for(i=0; i<3; i++) {
184                 mat[i] = rmat[i];
185                 mat[4 + i] = rmat[4 + i];
186                 mat[8 + i] = rmat[8 + i];
187         }
188
189         mat[0] *= obj->scale.x; mat[4] *= obj->scale.y; mat[8] *= obj->scale.z; mat[12] += obj->pos.x;
190         mat[1] *= obj->scale.x; mat[5] *= obj->scale.y; mat[9] *= obj->scale.z; mat[13] += obj->pos.y;
191         mat[2] *= obj->scale.x; mat[6] *= obj->scale.y; mat[10] *= obj->scale.z; mat[14] += obj->pos.z;
192
193         cgm_mpretranslate(mat, -obj->pivot.x, -obj->pivot.y, -obj->pivot.z);
194         /* that's basically: pivot * rotation * translation * scaling * -pivot */
195
196         cgm_mcopy(obj->inv_xform, mat);
197         cgm_minverse(obj->inv_xform);
198
199         obj->xform_valid = 1;
200 }