implementing build_bvh_sah
[cyberay] / src / level.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <float.h>
4 #include "level.h"
5 #include "treestore.h"
6 #include "mesh.h"
7
8 static int add_mesh_faces(struct bvhnode *bnode, struct mesh *mesh);
9
10 int load_level(struct level *lvl, const char *fname)
11 {
12         char *dirname, *ptr;
13         char path[256];
14         struct ts_node *root, *node;
15         struct scenefile scn;
16         struct mesh *mesh, *tail;
17
18         memset(lvl, 0, sizeof *lvl);
19         if(!(lvl->st_root = calloc(1, sizeof *lvl->st_root)) ||
20                         !(lvl->dyn_root = calloc(1, sizeof *lvl->dyn_root))) {
21                 free(lvl->st_root);
22                 fprintf(stderr, "load_level: failed to allocate bvh root nodes\n");
23                 return -1;
24         }
25
26         cgm_vcons(&lvl->st_root->aabb.vmin, FLT_MAX, FLT_MAX, FLT_MAX);
27         cgm_vcons(&lvl->st_root->aabb.vmax, -FLT_MAX, -FLT_MAX, -FLT_MAX);
28         lvl->dyn_root->aabb = lvl->st_root->aabb;
29
30         dirname = alloca(strlen(fname) + 1);
31         strcpy(dirname, fname);
32         if((ptr = strrchr(dirname, '/'))) {
33                 ptr[1] = 0;
34         } else {
35                 *dirname = 0;
36         }
37
38         if(!(root = ts_load(fname))) {
39                 fprintf(stderr, "load_level: failed to load: %s\n", fname);
40                 return -1;
41         }
42         if(strcmp(root->name, "level") != 0) {
43                 fprintf(stderr, "load_level: invalid level file %s, root is not \"level\"\n", fname);
44                 ts_free_tree(root);
45                 return -1;
46         }
47
48         node = root->child_list;
49         while(node) {
50                 if(strcmp(node->name, "scene") == 0) {
51                         if(!(fname = ts_get_attr_str(node, "file", 0))) {
52                                 fprintf(stderr, "load_level: ignoring \"scene\" without a \"file\" attribute\n");
53                                 goto cont;
54                         }
55                         snprintf(path, sizeof path, "%s%s", dirname, fname);
56                         printf("loading scene file: %s\n", path);
57
58                         if(load_scenefile(&scn, path) == -1) {
59                                 goto cont;
60                         }
61                         mesh = tail = scn.meshlist;
62                         while(mesh) {
63                                 add_mesh_faces(lvl->st_root, mesh);
64                                 tail = mesh;
65                                 mesh = mesh->next;
66                         }
67
68                         if(tail) {
69                                 tail->next = lvl->meshlist;
70                                 lvl->meshlist = scn.meshlist;
71                                 scn.meshlist = 0;
72                         }
73                         destroy_scenefile(&scn);
74                 }
75 cont:   node = node->next;
76         }
77
78         ts_free_tree(root);
79         return 0;
80 }
81
82 void destroy_level(struct level *lvl)
83 {
84         struct mesh *mesh;
85
86         free_bvh_tree(lvl->st_root);
87         free_bvh_tree(lvl->dyn_root);
88
89         while(lvl->meshlist) {
90                 mesh = lvl->meshlist;
91                 lvl->meshlist = lvl->meshlist->next;
92                 destroy_mesh(mesh);
93         }
94 }
95
96
97 int ray_level(cgm_ray *ray, struct level *lvl, float tmax, struct rayhit *hit)
98 {
99         int found = 0;
100         struct rayhit hit0;
101
102         if(!hit) {
103                 if(ray_bvhnode(ray, lvl->st_root, tmax, 0)) return 1;
104                 if(ray_bvhnode(ray, lvl->dyn_root, tmax, 0)) return 1;
105                 return 0;
106         }
107
108         hit0.t = FLT_MAX;
109         if(ray_bvhnode(ray, lvl->st_root, tmax, hit)) {
110                 hit0 = *hit;
111                 found = 1;
112         }
113         if(ray_bvhnode(ray, lvl->dyn_root, tmax, hit) && hit->t < hit0.t) {
114                 hit0 = *hit;
115                 found = 1;
116         }
117
118         if(found) {
119                 *hit = hit0;
120                 return 1;
121         }
122         return 0;
123 }
124
125 static void draw_level_rec(struct bvhnode *bn)
126 {
127         int i, j;
128         struct triangle *tri;
129         struct material *curmtl;
130         float color[4] = {0, 0, 0, 1};
131
132         if(!bn) return;
133
134         if(bn->faces) {
135                 curmtl = bn->faces[0]->mtl;
136
137                 glBegin(GL_TRIANGLES);
138                 for(i=0; i<bn->num_faces; i++) {
139                         tri = bn->faces[i];
140                         if(tri->mtl != curmtl) {
141                                 glEnd();
142                                 color[0] = tri->mtl->color.x;
143                                 color[1] = tri->mtl->color.y;
144                                 color[2] = tri->mtl->color.z;
145                                 glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
146                                 curmtl = tri->mtl;
147                                 glBegin(GL_TRIANGLES);
148                         }
149
150                         for(j=0; j<3; j++) {
151                                 glNormal3fv(&tri->v[j].norm.x);
152                                 glTexCoord2fv(&tri->v[j].tex.x);
153                                 glVertex3fv(&tri->v[j].pos.x);
154                         }
155                 }
156                 glEnd();
157         }
158
159         draw_level_rec(bn->left);
160         draw_level_rec(bn->right);
161 }
162
163 void draw_level(struct level *lvl)
164 {
165         draw_level_rec(lvl->st_root);
166         draw_level_rec(lvl->dyn_root);
167 }
168
169 static int add_mesh_faces(struct bvhnode *bnode, struct mesh *mesh)
170 {
171         int i, j, newsz;
172         void *tmp;
173         struct triangle *tri, **triptr;
174
175         newsz = bnode->num_faces + mesh->num_faces;
176         if(!(tmp = realloc(bnode->faces, newsz * sizeof *bnode->faces))) {
177                 fprintf(stderr, "append_polygons: failed to resize faces array to %d\n", newsz);
178                 return -1;
179         }
180         bnode->faces = tmp;
181         triptr = bnode->faces + bnode->num_faces;
182         bnode->num_faces = newsz;
183
184         tri = mesh->faces;
185         for(i=0; i<mesh->num_faces; i++) {
186                 *triptr++ = tri;
187                 for(j=0; j<3; j++) {
188                         cgm_vec3 *p = &tri->v[j].pos;
189                         if(p->x < bnode->aabb.vmin.x) bnode->aabb.vmin.x = p->x;
190                         if(p->x > bnode->aabb.vmax.x) bnode->aabb.vmax.x = p->x;
191                         if(p->y < bnode->aabb.vmin.y) bnode->aabb.vmin.y = p->y;
192                         if(p->y > bnode->aabb.vmax.y) bnode->aabb.vmax.y = p->y;
193                         if(p->z < bnode->aabb.vmin.z) bnode->aabb.vmin.z = p->z;
194                         if(p->z > bnode->aabb.vmax.z) bnode->aabb.vmax.z = p->z;
195                 }
196                 tri++;
197         }
198         return 0;
199 }